Search by

davidamunga / kenya-locations

davidamunga

Kenyan administrative divisions — counties, sub-counties, constituencies, wards, localities, and areas.

Package info

github.com/davidamunga/kenya-locations-php

Homepage

Issues

pkg:composer/davidamunga/kenya-locations

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

0.4.0 2026-11-05 17:34 UTC

This package is auto-updated.

Last update: 2026-11-05 17:41:29 UTC


README

Kenyan administrative divisions — counties, sub-counties, constituencies, wards, localities, and areas — as a typed PHP 8.2+ Composer library.

47 counties · 302 sub-counties · 290 constituencies · 1,450 wards · 917 localities · 1,829 areas

JSON is loaded lazily from the shared data/ files and cached for the process lifetime. No framework or service container is required.

This is the PHP counterpart to the JavaScript, React, Kotlin, Swift, and Dart libraries in the same kenya-locations project, all sourced from the same underlying dataset.

Development happens in the monorepo (packages/php). Packagist is served from the split repo kenya-locations-php.

Installation

composer require davidamunga/kenya-locations

Works in any PHP 8.2+ project — including WordPress themes and plugins.

There is no published WordPress plugin. In a Bedrock site, mu-plugin, or project-root Composer install:

use KenyaLocations\KenyaLocations;

$nairobi = KenyaLocations::getCountyByName('Nairobi');
$wards = KenyaLocations::getWardsInConstituency('Westlands');

A drop-in plugin (REST drill-down, County → Locality → Area fields, WooCommerce store address and checkout) lives in the monorepo at examples/wordpress. GitHub v* releases attach a zip with this library already vendored.

Usage

use KenyaLocations\KenyaLocations;
use KenyaLocations\SearchType;

$counties = KenyaLocations::getCounties();

$nairobi = KenyaLocations::getCountyByName('Nairobi');
echo $nairobi?->capital;          // Nairobi
echo $nairobi?->population2019;   // 4397073
echo $nairobi?->region;           // Nairobi

Drilling down

$constituencies = KenyaLocations::getConstituenciesInCounty('Nairobi');

$wards = KenyaLocations::getWardsInConstituency('Westlands');

$localities = KenyaLocations::getLocalitiesInCounty('Nairobi');

$areas = KenyaLocations::getAreasInLocality('Karen');

// Bura exists in three counties, so pass the county
$bura = KenyaLocations::getAreasInLocality('Bura', 'Garissa');

Resolving upward

$constituency = KenyaLocations::getConstituencyOfWard('Mountain view');
echo $constituency?->name; // Westlands

// Prefer a ward code when the name is reused (e.g. Township)
$byCode = KenyaLocations::getConstituencyOfWard('1370');

KenyaLocations::getCountyOfWard('1370');
KenyaLocations::getCountyOfConstituency('Westlands');
KenyaLocations::getLocalityOfArea('Gigiri');

// Same locality name in more than one county
$matches = KenyaLocations::getLocalitiesByName('Westlands');
$nairobiOnly = KenyaLocations::getLocality('Westlands', 'Nairobi');

Search

Search is case-insensitive and typo-tolerant, matching common misspellings via Levenshtein distance. It covers counties, sub-counties, constituencies, wards, localities, and areas.

$results = KenyaLocations::search('Nairob', limit: 20);
$wardsOnly = KenyaLocations::searchByType('West', SearchType::Ward);

foreach ($results as $result) {
    echo $result->type->value . ': ' . $result->name() . PHP_EOL;
}

Data model

County (47)
├── code, name, capital, areaKm2, population2019, region, postalCode, isoCode, aliases
├── Locality → Area          (informal addressing: estates, neighbourhoods)
│   917 localities · 1,829 areas
└── Constituency → Ward      (electoral / administrative)
    290 constituencies · 1,450 wards
    └── SubCounty (302)
Type Fields
County code, name, capital, areaKm2, population2019, region, postalCode, isoCode, aliases
SubCounty code, name, county, countyCode
Constituency code, name, county, countyCode
Ward code, name, constituency, constituencyCode, countyCode
Locality name, county, countyCode
Area name, locality, county, countyCode

isoCode is the ISO 3166-2:KE code (KE-30 for Nairobi). aliases are other names that resolve to the county, so getCountyByName('Nairobi City') and any county argument accept them. Relationships are joined on countyCode and constituencyCode, not on names.

API reference

Method Returns
getCounties() list<County>
getCountyByCode(code) ?County
getCountyByName(name) ?County
getSubCounties() list<SubCounty>
getSubCountyByCode(code) ?SubCounty
getSubCountyByName(name) ?SubCounty
getSubCountiesInCounty(nameOrCode) list<SubCounty>
getCountyOfSubCounty(nameOrCode) ?County
getConstituencies() list<Constituency>
getConstituencyByCode(code) ?Constituency
getConstituencyByName(name) ?Constituency
getConstituenciesInCounty(nameOrCode) list<Constituency>
getCountyOfConstituency(nameOrCode) ?County
getConstituencyOfWard(wardNameOrCode) ?Constituency
getWards() list<Ward>
getWardByCode(code) ?Ward
getWardByName(name) ?Ward
getWardsByName(name) list<Ward>
getWardsInConstituency(nameOrCode) list<Ward>
getWardsInCounty(nameOrCode) list<Ward>
getWardsInSubCounty(nameOrCode) list<Ward>
getCountyOfWard(wardNameOrCode) ?County
getSubCountyOfWard(wardNameOrCode) ?SubCounty
getLocalities() list<Locality>
getLocalityByName(name) ?Locality
getLocality(name, countyName?) ?Locality
getLocalitiesByName(name) list<Locality>
getLocalitiesInCounty(nameOrCode) list<Locality>
getCountyOfLocality(name) ?County
getAreas() list<Area>
getAreaByName(name) ?Area
getAreasByName(name) list<Area>
getAreasInLocality(localityName, county?) list<Area>
getAreasInCounty(nameOrCode) list<Area>
getCountyOfArea(name) ?County
getLocalityOfArea(name) ?Locality
search(query, limit: 20, types: null) list<SearchResult>
searchByType(query, type, limit: 20) list<SearchResult>

Name lookups are case-insensitive. When nothing matches exactly, a looser match that ignores spaces, hyphens, apostrophes and accents is tried (Muranga finds Murang'a). Codes are zero-padded, so '47' finds county 047. County, constituency, and sub-county arguments accept a name or an administrative code.

Singular by-name lookups return null when a name is reused (for example the Township ward or the Makadara locality) instead of an arbitrary record. Use the plural ...ByName methods, a ward code, or pass a county to getLocality() / getAreasInLocality() in that case.

Local development

From this directory:

composer install
composer test

The package ships a copy of the monorepo data/*.json files in data/ so a Packagist install is self-contained. After editing the shared JSON at the repo root, refresh that copy:

composer copy-data

CI fails if the copy is stale. Runtime also falls back to the monorepo data/ directory, so local tests work even before you copy. No codegen step is required.

Contributing

Data corrections and additions belong in the shared data/ JSON files at the root of the kenya-locations monorepo. See the main repo's CONTRIBUTING guide.

License

MIT © David Amunga

Data sourced from the Independent Electoral and Boundaries Commission (IEBC) and Kenya National Bureau of Statistics (KNBS).