From 8c87fdd55e6c09d203f9aa0fc523eabf13b40722 Mon Sep 17 00:00:00 2001 From: titustum Date: Sun, 24 Aug 2025 02:31:34 +0300 Subject: [PATCH 1/8] Add Kenyan-specific Faker providers for Company, Internet, Payment, Person, and PhoneNumber - Implemented a Company provider with a list of well-known Kenyan companies and suffixes. - Created an Internet provider featuring common email domains and TLDs used in Kenya. - Developed a Payment provider that includes mobile money services, banks, and Paybill numbers. - Added a Person provider with a diverse set of Kenyan first names and surnames. - Introduced a PhoneNumber provider with formats for local and international phone numbers. --- src/Provider/en_KE/Address.php | 339 +++++++++ src/Provider/en_KE/Company.php | 111 +++ src/Provider/en_KE/Internet.php | 84 ++ src/Provider/en_KE/Payment.php | 113 +++ src/Provider/en_KE/Person.php | 1136 ++++++++++++++++++++++++++++ src/Provider/en_KE/PhoneNumber.php | 33 + 6 files changed, 1816 insertions(+) create mode 100644 src/Provider/en_KE/Address.php create mode 100644 src/Provider/en_KE/Company.php create mode 100644 src/Provider/en_KE/Internet.php create mode 100644 src/Provider/en_KE/Payment.php create mode 100644 src/Provider/en_KE/Person.php create mode 100644 src/Provider/en_KE/PhoneNumber.php diff --git a/src/Provider/en_KE/Address.php b/src/Provider/en_KE/Address.php new file mode 100644 index 0000000000..be1a1d7f62 --- /dev/null +++ b/src/Provider/en_KE/Address.php @@ -0,0 +1,339 @@ +city(); + } + + /** + * Generates a random Kenyan postal code. + * Kenyan postal codes typically fall into defined numeric ranges based on region. + * + * @return string Postal code as a 5-digit zero-padded string + */ + public static function postcode() + { + $ranges = [ + [100, 999], // Nairobi, Mombasa, and other central areas (e.g. 00100-00999) + [10000, 19999], // Central and Eastern regions + [20000, 29999], // Rift Valley region + [30000, 39999], // Western region + [40000, 49999], // Nyanza region + ]; + + $range = static::randomElement($ranges); + + $postcode = static::numberBetween($range[0], $range[1]); + + // Ensure postal code is zero-padded to 5 digits + return str_pad($postcode, 5, '0', STR_PAD_LEFT); + } + + /** + * Returns a random street name from the Kenyan streets list. + * + * @return string + */ + public function streetName() + { + return static::randomElement(static::$street); + } + + /** + * Generates a street address combining a random street number with a street name. + * + * @return string Full street address + */ + public function streetAddress() + { + $number = $this->numberBetween(1, 200); + return $number . ' ' . $this->streetName(); + } +} diff --git a/src/Provider/en_KE/Company.php b/src/Provider/en_KE/Company.php new file mode 100644 index 0000000000..1a8a59cfa4 --- /dev/null +++ b/src/Provider/en_KE/Company.php @@ -0,0 +1,111 @@ +generator->boolean(50)) { + $suffix = static::randomElement(static::$companySuffix); + + // Append suffix only if it's not already part of the company name (case-insensitive) + if (stripos($company, $suffix) === false) { + $company .= ' ' . $suffix; + } + } + + return $company; + } +} diff --git a/src/Provider/en_KE/Internet.php b/src/Provider/en_KE/Internet.php new file mode 100644 index 0000000000..59046ee27c --- /dev/null +++ b/src/Provider/en_KE/Internet.php @@ -0,0 +1,84 @@ +freeEmail(); + } +} diff --git a/src/Provider/en_KE/Payment.php b/src/Provider/en_KE/Payment.php new file mode 100644 index 0000000000..a218b4e9ef --- /dev/null +++ b/src/Provider/en_KE/Payment.php @@ -0,0 +1,113 @@ + static::mobileMoneyProvider(), + 'number' => static::mobileMoneyNumber(), + 'transaction_code' => static::transactionCode(), + 'amount' => static::randomFloat(2, 10, 10000), + 'paybill' => static::paybillNumber(), + 'date' => date('Y-m-d H:i:s', time()), + ]; + } +} diff --git a/src/Provider/en_KE/Person.php b/src/Provider/en_KE/Person.php new file mode 100644 index 0000000000..37d6d7bafa --- /dev/null +++ b/src/Provider/en_KE/Person.php @@ -0,0 +1,1136 @@ + Date: Sun, 24 Aug 2025 03:34:40 +0300 Subject: [PATCH 2/8] Fix namespace declarations in Kenyan Faker provider classes and add tests for Address, Company, Internet, Payment, Person, and PhoneNumber --- src/Provider/en_KE/Address.php | 2 +- src/Provider/en_KE/Company.php | 2 +- src/Provider/en_KE/Internet.php | 2 +- src/Provider/en_KE/Payment.php | 5 +- src/Provider/en_KE/Person.php | 2 +- src/Provider/en_KE/PhoneNumber.php | 2 +- test/Provider/en_KE/AddressTest.php | 88 ++++++++++++++++++++++++ test/Provider/en_KE/CompanyTest.php | 70 +++++++++++++++++++ test/Provider/en_KE/InternetTest.php | 91 +++++++++++++++++++++++++ test/Provider/en_KE/PaymentTest.php | 89 ++++++++++++++++++++++++ test/Provider/en_KE/PersonTest.php | 34 +++++++++ test/Provider/en_KE/PhoneNumberTest.php | 49 +++++++++++++ 12 files changed, 429 insertions(+), 7 deletions(-) create mode 100644 test/Provider/en_KE/AddressTest.php create mode 100644 test/Provider/en_KE/CompanyTest.php create mode 100644 test/Provider/en_KE/InternetTest.php create mode 100644 test/Provider/en_KE/PaymentTest.php create mode 100644 test/Provider/en_KE/PersonTest.php create mode 100644 test/Provider/en_KE/PhoneNumberTest.php diff --git a/src/Provider/en_KE/Address.php b/src/Provider/en_KE/Address.php index be1a1d7f62..ab0c67e128 100644 --- a/src/Provider/en_KE/Address.php +++ b/src/Provider/en_KE/Address.php @@ -1,6 +1,6 @@ = 100 && $codeNum <= 999) || + ($codeNum >= 10000 && $codeNum <= 19999) || + ($codeNum >= 20000 && $codeNum <= 29999) || + ($codeNum >= 30000 && $codeNum <= 39999) || + ($codeNum >= 40000 && $codeNum <= 49999) + ); + + self::assertTrue($valid, "Postcode {$postcode} is not in a valid range"); + } + + public function testCountyIsAValidString(): void + { + $county = $this->faker->county; + + self::assertNotEmpty($county); + self::assertIsString($county); + } + + public function testRegionIsAValidString(): void + { + $region = $this->faker->region; + + self::assertNotEmpty($region); + self::assertIsString($region); + } + + public function testCityIsAValidString(): void + { + $city = $this->faker->city; + + self::assertNotEmpty($city); + self::assertIsString($city); + } + + public function testTownIsAliasForCity(): void + { + $town = $this->faker->town; + $city = $this->faker->city; + + self::assertNotEmpty($town); + self::assertIsString($town); + } + + public function testStreetNameIsAValidString(): void + { + $street = $this->faker->streetName; + + self::assertNotEmpty($street); + self::assertIsString($street); + } + + public function testStreetAddressIsFormattedCorrectly(): void + { + $streetAddress = $this->faker->streetAddress; + + self::assertNotEmpty($streetAddress); + self::assertIsString($streetAddress); + self::assertMatchesRegularExpression('/^\d+ .+$/', $streetAddress); + } + + protected function getProviders(): iterable + { + yield new Address($this->faker); + } +} diff --git a/test/Provider/en_KE/CompanyTest.php b/test/Provider/en_KE/CompanyTest.php new file mode 100644 index 0000000000..0fba63bdf1 --- /dev/null +++ b/test/Provider/en_KE/CompanyTest.php @@ -0,0 +1,70 @@ +faker->company(); + + self::assertNotEmpty($company); + self::assertIsString($company); + + // The company name should start with one of the known base company names + $baseNames = (new \ReflectionClass(Company::class))->getStaticPropertyValue('companyName'); + $suffixes = (new \ReflectionClass(Company::class))->getStaticPropertyValue('companySuffix'); + + // Check company starts with one of the base names (case-insensitive) + $foundBase = false; + foreach ($baseNames as $baseName) { + if (stripos($company, $baseName) === 0) { + $foundBase = true; + break; + } + } + self::assertTrue($foundBase, "Company name should start with a known base company name"); + + // Optionally check suffix presence (suffix may or may not be appended) + $hasSuffix = false; + foreach ($suffixes as $suffix) { + // Case insensitive suffix check at the end + if (stripos($company, $suffix) !== false) { + $hasSuffix = true; + break; + } + } + + // The suffix is optional, so no assertion here about it existing + // But if suffix exists, it must be one of the known suffixes + if ($hasSuffix) { + $matchedSuffix = false; + foreach ($suffixes as $suffix) { + if (stripos($company, $suffix) !== false) { + $matchedSuffix = true; + break; + } + } + self::assertTrue($matchedSuffix, "If suffix exists, it must be one of the known suffixes"); + } + } + + protected function getProviders(): iterable + { + yield new Person($this->faker); + yield new Base($this->faker); + yield new Miscellaneous($this->faker); + yield new Company($this->faker); + } +} diff --git a/test/Provider/en_KE/InternetTest.php b/test/Provider/en_KE/InternetTest.php new file mode 100644 index 0000000000..18180b384f --- /dev/null +++ b/test/Provider/en_KE/InternetTest.php @@ -0,0 +1,91 @@ +faker->domainName(); + + self::assertNotEmpty($domain); + self::assertIsString($domain); + + $tlds = (new \ReflectionClass(Internet::class))->getStaticPropertyValue('tld'); + + $found = false; + foreach ($tlds as $tld) { + if (str_ends_with($domain, '.' . $tld)) { + $found = true; + break; + } + } + + self::assertTrue($found, 'Domain name should end with a valid Kenyan TLD'); + } + + public function testFreeEmailHasValidDomain(): void + { + $email = $this->faker->freeEmail(); + + self::assertNotEmpty($email); + self::assertIsString($email); + + $freeEmailDomains = (new \ReflectionClass(Internet::class))->getStaticPropertyValue('freeEmailDomain'); + + $domain = substr(strrchr($email, '@'), 1); + self::assertContains($domain, $freeEmailDomains, 'Free email domain should be one of the Kenyan free email domains'); + } + + public function testCompanyEmailHasValidIspDomain(): void + { + $email = $this->faker->companyEmail(); + + self::assertNotEmpty($email); + self::assertIsString($email); + + $ispDomains = (new \ReflectionClass(Internet::class))->getStaticPropertyValue('ispDomains'); + + $domain = substr(strrchr($email, '@'), 1); + self::assertContains($domain, $ispDomains, 'Company email domain should be one of the Kenyan ISP domains'); + } + + public function testFreeEmailIsValid(): void + { + $email = $this->faker->freeEmail(); + + self::assertNotEmpty($email); + self::assertIsString($email); + + // Validate email format + self::assertMatchesRegularExpression( + '/^[^\s@]+@[^\s@]+\.[^\s@]+$/', + $email, + 'freeEmail() should return a valid email address' + ); + + // Check domain is in known free email domains + $freeEmailDomains = (new \ReflectionClass(Internet::class))->getStaticPropertyValue('freeEmailDomain'); + $domain = substr(strrchr($email, '@'), 1); + + self::assertContains( + $domain, + $freeEmailDomains, + 'Free email domain should be one of the known free email domains' + ); + } + + + protected function getProviders(): iterable + { + yield new \Faker\Provider\Person($this->faker); + yield new Internet($this->faker); + } +} diff --git a/test/Provider/en_KE/PaymentTest.php b/test/Provider/en_KE/PaymentTest.php new file mode 100644 index 0000000000..6ca861f116 --- /dev/null +++ b/test/Provider/en_KE/PaymentTest.php @@ -0,0 +1,89 @@ +getStaticPropertyValue('mobileMoneyProviders'); + + self::assertContains($provider, $providers); + } + + public function testPaybillNumberIsValid(): void + { + $paybill = Payment::paybillNumber(); + + $paybills = (new \ReflectionClass(Payment::class))->getStaticPropertyValue('paybillNumbers'); + + self::assertContains($paybill, $paybills); + } + + public function testTransactionCodeFormat(): void + { + $code = Payment::transactionCode(); + + self::assertMatchesRegularExpression('/^[A-Z]{2}\d[A-Z]\d[A-Z]{2}\d{2}$/', $code); + self::assertEquals(10, strlen($code)); + } + + public function testBankIsValid(): void + { + $bank = Payment::bank(); + + $banks = (new \ReflectionClass(Payment::class))->getStaticPropertyValue('banks'); + + self::assertContains($bank, $banks); + } + + public function testBankAccountNumberFormat(): void + { + $accountNumber = Payment::bankAccountNumber(); + + self::assertMatchesRegularExpression('/^01\d{10}$/', $accountNumber); + self::assertEquals(12, strlen($accountNumber)); + } + + public function testMobileMoneyNumberFormat(): void + { + $mobileNumber = Payment::mobileMoneyNumber(); + + self::assertMatchesRegularExpression('/^07\d{8}$/', $mobileNumber); + self::assertEquals(10, strlen($mobileNumber)); + } + + public function testMobileMoneyStatementStructure(): void + { + $statement = Payment::mobileMoneyStatement(); + + self::assertIsArray($statement); + self::assertArrayHasKey('provider', $statement); + self::assertArrayHasKey('number', $statement); + self::assertArrayHasKey('transaction_code', $statement); + self::assertArrayHasKey('amount', $statement); + self::assertArrayHasKey('paybill', $statement); + self::assertArrayHasKey('date', $statement); + + $this->assertContains($statement['provider'], (new \ReflectionClass(Payment::class))->getStaticPropertyValue('mobileMoneyProviders')); + self::assertMatchesRegularExpression('/^07\d{8}$/', $statement['number']); + self::assertMatchesRegularExpression('/^[A-Z]{2}\d[A-Z]\d[A-Z]{2}\d{2}$/', $statement['transaction_code']); + self::assertIsFloat($statement['amount']); + self::assertContains($statement['paybill'], (new \ReflectionClass(Payment::class))->getStaticPropertyValue('paybillNumbers')); + self::assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $statement['date']); + } + + protected function getProviders(): iterable + { + yield new Payment($this->faker); + } +} diff --git a/test/Provider/en_KE/PersonTest.php b/test/Provider/en_KE/PersonTest.php new file mode 100644 index 0000000000..050871a851 --- /dev/null +++ b/test/Provider/en_KE/PersonTest.php @@ -0,0 +1,34 @@ +faker->firstNameMale(); + self::assertIsString($name); + self::assertNotEmpty($name); + } + } + + public function testFirstNameFemale(): void + { + for ($i = 0; $i < 100; ++$i) { + $name = $this->faker->firstNameFemale(); + self::assertIsString($name); + self::assertNotEmpty($name); + } + } + + protected function getProviders(): iterable + { + yield new Person($this->faker); + } +} diff --git a/test/Provider/en_KE/PhoneNumberTest.php b/test/Provider/en_KE/PhoneNumberTest.php new file mode 100644 index 0000000000..6c005693b5 --- /dev/null +++ b/test/Provider/en_KE/PhoneNumberTest.php @@ -0,0 +1,49 @@ +faker->phoneNumber; + + self::assertNotEmpty($phoneNumber); + self::assertIsString($phoneNumber); + + // The formats defined in PhoneNumber::$formats are: + // +2547########, 07########, +2541########, 01######## + // We check that the generated number matches one of these patterns: + $pattern = '/^(?:\+2547\d{8}|07\d{8}|\+2541\d{8}|01\d{8})$/'; + + self::assertMatchesRegularExpression($pattern, $phoneNumber); + } + + public function testE164PhoneNumberFormats(): void + { + // The provider does not expose a separate e164 method by default, + // but we can test if the phoneNumber matches e164 formats too. + $phoneNumber = $this->faker->phoneNumber; + + self::assertNotEmpty($phoneNumber); + self::assertIsString($phoneNumber); + + // The E.164 formats in PhoneNumber::$e164Formats are: + // +2547########, +2541########, +25410#######, +25411####### + $pattern = '/^\+254(?:7\d{8}|1\d{8}|10\d{7}|11\d{7})$/'; + + self::assertMatchesRegularExpression($pattern, $phoneNumber); + } + + protected function getProviders(): iterable + { + yield new PhoneNumber($this->faker); + } +} From ca4cd6a0f5e41fc025958cee559a4e4d18320837 Mon Sep 17 00:00:00 2001 From: titustum Date: Sun, 24 Aug 2025 03:52:40 +0300 Subject: [PATCH 3/8] Fix timezone configuration in phpunit.xml.dist and update regex patterns in PaymentTest --- phpunit.xml.dist | 1 + test/Provider/en_KE/PaymentTest.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 25e983b9de..a57e8f96c0 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,6 +15,7 @@ + diff --git a/test/Provider/en_KE/PaymentTest.php b/test/Provider/en_KE/PaymentTest.php index 6ca861f116..f16321df15 100644 --- a/test/Provider/en_KE/PaymentTest.php +++ b/test/Provider/en_KE/PaymentTest.php @@ -33,7 +33,7 @@ public function testTransactionCodeFormat(): void { $code = Payment::transactionCode(); - self::assertMatchesRegularExpression('/^[A-Z]{2}\d[A-Z]\d[A-Z]{2}\d{2}$/', $code); + self::assertMatchesRegularExpression('/^[A-Z]{2}\d[A-Z]\d{2}[A-Z]{2}\d{2}$/', $code); self::assertEquals(10, strlen($code)); } @@ -76,7 +76,7 @@ public function testMobileMoneyStatementStructure(): void $this->assertContains($statement['provider'], (new \ReflectionClass(Payment::class))->getStaticPropertyValue('mobileMoneyProviders')); self::assertMatchesRegularExpression('/^07\d{8}$/', $statement['number']); - self::assertMatchesRegularExpression('/^[A-Z]{2}\d[A-Z]\d[A-Z]{2}\d{2}$/', $statement['transaction_code']); + self::assertMatchesRegularExpression('/^[A-Z]{2}\d[A-Z]\d{2}[A-Z]{2}\d{2}$/', $statement['transaction_code']); self::assertIsFloat($statement['amount']); self::assertContains($statement['paybill'], (new \ReflectionClass(Payment::class))->getStaticPropertyValue('paybillNumbers')); self::assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $statement['date']); From 7a98d1bf1db78ab3ca4bb57d58f8c2f1b221b97a Mon Sep 17 00:00:00 2001 From: titustum Date: Sun, 24 Aug 2025 17:22:15 +0300 Subject: [PATCH 4/8] Refactor Kenyan male and female first names in Person provider with comprehensive lists --- src/Provider/en_KE/Person.php | 979 +++++++++++++++++++++++++++++++--- 1 file changed, 894 insertions(+), 85 deletions(-) diff --git a/src/Provider/en_KE/Person.php b/src/Provider/en_KE/Person.php index 39fa33eac6..ff3496f0e0 100644 --- a/src/Provider/en_KE/Person.php +++ b/src/Provider/en_KE/Person.php @@ -13,94 +13,906 @@ class Person extends \Faker\Provider\Person { /** - * @var array Kenyan male first names (Christian and Islamic origins). + * @var array Kenyan male first names. + * Source: https://forebears.io/kenya/forenames */ protected static $firstNameMale = [ - 'John', - 'David', - 'Peter', - 'James', - 'Joseph', - 'Samuel', - 'Ahmed', - 'Ali', - 'Hassan', - 'Salim', - 'Isaac', - 'Michael', - 'Paul', - 'Simon', - 'Abdullahi', - 'Omar', - 'Daniel', - 'George', - 'Stephen', - 'Elijah', - 'Andrew', - 'Noah', - 'Nicholas', - 'Francis', - 'Fredrick', - 'Caleb', - 'Brian', - 'Anthony', - 'Martin', - 'Ezekiel', - 'Emmanuel', - 'Collins', - 'Ken', - 'Eric', - 'Mohamed', - 'Farah', - 'Yusuf', - 'Ibrahim', - 'Abubakar', + "Aaron", + "Abdalla", + "Abdallah", + "Abdi", + "Abdihakim", + "Abdul", + "Abednego", + "Abel", + "Abraham", + "Abubakar", + "Adam", + "Adan", + "Aden", + "Aftin", + "Aggrey", + "Ahmed", + "Albert", + "Alex", + "Alexander", + "Alfred", + "Ali", + "Allan", + "Alphonce", + "Alvin", + "Amani", + "Ambrose", + "Amin", + "Amon", + "Amos", + "Anderson", + "Andrew", + "Anthony", + "Antony", + "Arnold", + "Aron", + "Athman", + "Athuman", + "Augustine", + "Austine", + "Ayub", + "Bahati", + "Bakari", + "Baraka", + "Barasa", + "Benard", + "Benedict", + "Benjamin", + "Benson", + "Bernard", + "Bett", + "Billy", + "Boaz", + "Bonface", + "Boniface", + "Bramwel", + "Bravin", + "Brian", + "Bryan", + "Caleb", + "Calvin", + "Calvince", + "Carlos", + "Charles", + "Charo", + "Chege", + "Cheruiyot", + "Chris", + "Christopher", + "Clement", + "Cleophas", + "Clifford", + "Clinton", + "Collince", + "Collins", + "Cornelius", + "Cosmas", + "Cyrus", + "Dalmas", + "Dan", + "Dancan", + "Dancun", + "Daniel", + "Danson", + "Daud", + "Daudi", + "David", + "Davis", + "Denis", + "Dennis", + "Derick", + "Derrick", + "Dickson", + "Dismas", + "Dominic", + "Donald", + "Douglas", + "Duke", + "Duncan", + "Edgar", + "Edward", + "Edwin", + "Elias", + "Elijah", + "Elisha", + "Eliud", + "Elkana", + "Elly", + "Elphas", + "Elvis", + "Emanuel", + "Emmanuel", + "Enock", + "Ephantus", + "Erastus", + "Eric", + "Erick", + "Ernest", + "Eugene", + "Eugine", + "Evance", + "Evans", + "Ezekiel", + "Ezra", + "Feisal", + "Felix", + "Festus", + "Fidel", + "Fidelis", + "Francis", + "Frank", + "Franklin", + "Frankline", + "Fred", + "Fredrick", + "Gabriel", + "Gathoni", + "Gedion", + "Geoffrey", + "Geofrey", + "George", + "Gerald", + "Gibson", + "Gideon", + "Gilbert", + "Gitau", + "Githinji", + "Gitonga", + "Godfrey", + "Godwin", + "Gordon", + "Gregory", + "Hamisi", + "Hamza", + "Haron", + "Harrison", + "Harun", + "Hassan", + "Henry", + "Hesbon", + "Hezron", + "Hillary", + "Hosea", + "Humphrey", + "Hussein", + "Ian", + "Ibrahim", + "Idris", + "Innocent", + "Irungu", + "Isaac", + "Isaack", + "Isack", + "Isaiah", + "Ismael", + "Ismail", + "Issa", + "Issack", + "Jack", + "Jackson", + "Jacob", + "Jairus", + "Jamal", + "James", + "Japhet", + "Japheth", + "Jared", + "Javan", + "Jeff", + "Jeremiah", + "Jesse", + "Jimmy", + "Joash", + "Job", + "Joe", + "Joel", + "John", + "Johnson", + "Johnstone", + "Jonah", + "Jonathan", + "Joram", + "Joseph", + "Josephat", + "Joshua", + "Josiah", + "Josphat", + "Julius", + "Juma", + "Justin", + "Justine", + "Justus", + "Kamau", + "Kanini", + "Karani", + "Karanja", + "Karimi", + "Kariuki", + "Kassim", + "Katana", + "Keith", + "Kelly", + "Kelvin", + "Kemboi", + "Ken", + "Kenedy", + "Keneth", + "Kennedy", + "Kenneth", + "Kevin", + "Kiarie", + "Kibet", + "Kilonzo", + "Kimani", + "Kimanzi", + "Kimathi", + "Kimeu", + "Kimutai", + "Kinuthia", + "Kinyanjui", + "Kinyua", + "Kioko", + "Kipchirchir", + "Kipchumba", + "Kipkemboi", + "Kipkemoi", + "Kipkirui", + "Kipkoech", + "Kipkorir", + "Kipkosgei", + "Kipkurui", + "Kiplagat", + "Kiplangat", + "Kiplimo", + "Kipngeno", + "Kipngetich", + "Kiprono", + "Kiprop", + "Kiprotich", + "Kipruto", + "Kipsang", + "Kiptoo", + "Kipyegon", + "Kirui", + "Koech", + "Korir", + "Kosgei", + "Kuria", + "Kyalo", + "Laban", + "Lameck", + "Langat", + "Lawrence", + "Leonard", + "Levis", + "Lewis", + "Liban", + "Linus", + "Livingstone", + "Lucas", + "Lucky", + "Luka", + "Luke", + "Macharia", + "Maina", + "Maingi", + "Makau", + "Makokha", + "Makori", + "Mark", + "Martin", + "Marvin", + "Masika", + "Masinde", + "Mathew", + "Mathias", + "Maurice", + "Maxwel", + "Maxwell", + "Mbithi", + "Mboya", + "Mbugua", + "Mburu", + "Melvin", + "Meshack", + "Micah", + "Michael", + "Micheal", + "Mike", + "Milton", + "Mohamed", + "Mohammed", + "Mohamud", + "Momanyi", + "Morgan", + "Morris", + "Moses", + "Muchiri", + "Muema", + "Mueni", + "Mugambi", + "Mugendi", + "Mugo", + "Muia", + "Muiruri", + "Mukhwana", + "Muli", + "Mulwa", + "Mumbi", + "Mumo", + "Munene", + "Mungai", + "Munyao", + "Munyoki", + "Muriithi", + "Murimi", + "Murithi", + "Muriuki", + "Musa", + "Musau", + "Musee", + "Musembi", + "Mustafa", + "Musyoka", + "Musyoki", + "Mutai", + "Muthama", + "Muthengi", + "Muthoka", + "Muthoni", + "Muthui", + "Mutie", + "Mutinda", + "Mutiso", + "Mutisya", + "Mutua", + "Mutuku", + "Mutunga", + "Muturi", + "Mwangangi", + "Mwangi", + "Mwaniki", + "Mwanzia", + "Mwaura", + "Mwende", + "Mwendwa", + "Mwikali", + "Mwiti", + "Nahashon", + "Nathan", + "Ndegwa", + "Nderitu", + "Ndirangu", + "Nduku", + "Ndung'u", + "Ndunge", + "Ndungu", + "Nduta", + "Nehemiah", + "Nelson", + "Newton", + "Ng'ang'a", + "Nganga", + "Ngari", + "Ngigi", + "Ngugi", + "Nicholas", + "Nicholus", + "Nickson", + "Nicodemus", + "Nixon", + "Njagi", + "Njenga", + "Njeri", + "Njeru", + "Njiru", + "Njogu", + "Njoki", + "Njoroge", + "Njue", + "Njuguna", + "Noah", + "Noel", + "Noor", + "Nuru", + "Nyaga", + "Nyakundi", + "Nyamai", + "Nyambura", + "Nyawira", + "Nyongesa", + "Nzioka", + "Nzomo", + "Obed", + "Ochieng", + "Odhiambo", + "Odongo", + "Oduor", + "Okello", + "Okoth", + "Okumu", + "Oliver", + "Oloo", + "Oluoch", + "Omar", + "Omari", + "Omondi", + "Ondieki", + "Onesmus", + "Onyango", + "Opiyo", + "Oscar", + "Osman", + "Otieno", + "Ouma", + "Owen", + "Owino", + "Owuor", + "Pascal", + "Patrick", + "Paul", + "Peter", + "Peterson", + "Phelix", + "Philemon", + "Philip", + "Phillip", + "Phineas", + "Pius", + "Rajab", + "Rama", + "Ramadhan", + "Raphael", + "Rashid", + "Raymond", + "Reagan", + "Reuben", + "Richard", + "Riziki", + "Robert", + "Robin", + "Robinson", + "Rodgers", + "Rogers", + "Ronald", + "Ronny", + "Rono", + "Rotich", + "Roy", + "Ryan", + "Said", + "Saidi", + "Salim", + "Sam", + "Sammy", + "Samson", + "Samuel", + "Samwel", + "Seth", + "Shaban", + "Shadrack", + "Shem", + "Sifuna", + "Sila", + "Silas", + "Silvester", + "Simeon", + "Simion", + "Simiyu", + "Simon", + "Solomon", + "Stanley", + "Stephen", + "Stephene", + "Steve", + "Steven", + "Suleiman", + "Swaleh", + "Sylvester", + "Teddy", + "Terry", + "Thomas", + "Thuo", + "Timothy", + "Titus", + "Tobias", + "Tom", + "Tonny", + "Tony", + "Tsuma", + "Tyson", + "Valentine", + "Victor", + "Vincent", + "Vitalis", + "Wachira", + "Wafula", + "Wainaina", + "Wairimu", + "Walter", + "Wamalwa", + "Wambua", + "Wambugu", + "Wambui", + "Wangari", + "Wangila", + "Wangui", + "Wanja", + "Wanjala", + "Wanjiku", + "Wanjiru", + "Wanjohi", + "Wanyama", + "Wanyonyi", + "Washington", + "Waweru", + "Wekesa", + "Weldon", + "Wesley", + "Wesonga", + "Wilfred", + "William", + "Willis", + "Willy", + "Wilson", + "Wycliff", + "Wycliffe", + "Yahya", + "Yunis", + "Yussuf", + "Yusuf", + "Zablon", + "Zacharia", + "Zachary", + "Zakaria", + "Zakayo" ]; /** - * @var array Kenyan female first names (Christian and Islamic origins). + * @var array Kenyan female first names. + * Source: https://forebears.io/kenya/forenames */ protected static $firstNameFemale = [ - 'Jane', - 'Mary', - 'Grace', - 'Mercy', - 'Esther', - 'Faith', - 'Rebecca', - 'Zainab', - 'Amina', - 'Fatuma', - 'Dorothy', - 'Christine', - 'Lilian', - 'Sarah', - 'Hawa', - 'Huda', - 'Naomi', - 'Ann', - 'Elizabeth', - 'Ruth', - 'Janet', - 'Deborah', - 'Joyce', - 'Alice', - 'Catherine', - 'Beatrice', - 'Sharon', - 'Irene', - 'Agnes', - 'Eunice', - 'Jackline', - 'Millicent', - 'Gloria', - 'Winnie', - 'Tabitha', - 'Halima', - 'Mariam', - 'Nadia', - 'Latifa', - 'Shukri', + "Abigael", + "Achieng", + "Agnes", + "Aisha", + "Akinyi", + "Akoth", + "Alice", + "Amina", + "Anastacia", + "Angela", + "Angeline", + "Anita", + "Ann", + "Anna", + "Annah", + "Annastacia", + "Anne", + "Annet", + "Anyango", + "Asha", + "Atieno", + "Beatrice", + "Belinda", + "Benta", + "Beryl", + "Beth", + "Betty", + "Bilha", + "Bosibori", + "Brenda", + "Brendah", + "Bridget", + "Bridgit", + "Brigid", + "Caren", + "Carol", + "Caroline", + "Carolyne", + "Carren", + "Catherine", + "Cecilia", + "Celestine", + "Centrine", + "Charity", + "Chebet", + "Chelangat", + "Chepkemoi", + "Chepkoech", + "Chepkorir", + "Cherotich", + "Christabel", + "Christine", + "Claire", + "Clara", + "Clare", + "Consolata", + "Cynthia", + "Daisy", + "Damaris", + "Debora", + "Deborah", + "Diana", + "Dianah", + "Dinah", + "Dolphine", + "Dorcas", + "Dorcus", + "Doreen", + "Dorine", + "Doris", + "Dorothy", + "Eddah", + "Edinah", + "Edith", + "Edna", + "Elizabeth", + "Elosy", + "Emilly", + "Emily", + "Emma", + "Emmaculate", + "Emmah", + "Emmy", + "Esther", + "Eunice", + "Eva", + "Evaline", + "Evalyne", + "Evelyne", + "Everline", + "Everlyne", + "Faith", + "Fancy", + "Farida", + "Fatuma", + "Felista", + "Felister", + "Felistus", + "Fiona", + "Flora", + "Florence", + "Francisca", + "Frida", + "Fridah", + "Gesare", + "Getrude", + "Gladys", + "Gloria", + "Grace", + "Habiba", + "Hadija", + "Hafsa", + "Halima", + "Hannah", + "Harriet", + "Hawa", + "Hellen", + "Hilda", + "Hildah", + "Immaculate", + "Irene", + "Irine", + "Ivone", + "Ivy", + "Jacinta", + "Jackline", + "Jael", + "Jamila", + "Jane", + "Janet", + "Janeth", + "Jebet", + "Jecinta", + "Jedidah", + "Jelagat", + "Jemimah", + "Jemutai", + "Jeniffer", + "Jenipher", + "Jennifer", + "Jepchirchir", + "Jepchumba", + "Jepkemoi", + "Jepkoech", + "Jepkorir", + "Jepkosgei", + "Jeptoo", + "Jerono", + "Jerop", + "Jerotich", + "Jeruto", + "Jesca", + "Joan", + "Josephine", + "Josphine", + "Joy", + "Joyce", + "Joyline", + "Judith", + "Judy", + "Julia", + "Juliana", + "Juliet", + "June", + "Karen", + "Kemuma", + "Kemunto", + "Kerubo", + "Keziah", + "Khadija", + "Kwamboka", + "Laura", + "Laureen", + "Lavender", + "Leah", + "Leila", + "Lenah", + "Lilian", + "Lillian", + "Linah", + "Linda", + "Lindah", + "Linet", + "Loice", + "Loise", + "Lorine", + "Lorna", + "Lucia", + "Lucy", + "Lydia", + "Lydiah", + "Lynn", + "Magdalene", + "Magdaline", + "Maimuna", + "Makena", + "Margaret", + "Margret", + "Maria", + "Mariam", + "Marion", + "Martha", + "Mary", + "Maryann", + "Maryanne", + "Maureen", + "Maurine", + "Melvine", + "Mercy", + "Mercyline", + "Metrine", + "Milcah", + "Mildred", + "Milka", + "Milkah", + "Millicent", + "Miriam", + "Mirriam", + "Mitchelle", + "Monica", + "Monicah", + "Moraa", + "Moreen", + "Morine", + "Moureen", + "Mourine", + "Mwanajuma", + "Mwanamisi", + "Nafula", + "Naliaka", + "Nancy", + "Nanjala", + "Naom", + "Naomi", + "Naomy", + "Natasha", + "Neema", + "Nekesa", + "Nelima", + "Nelly", + "Norah", + "Nyaboke", + "Nyanchama", + "Pamela", + "Patience", + "Patricia", + "Pauline", + "Penina", + "Peninah", + "Peris", + "Phanice", + "Philis", + "Philomena", + "Phoebe", + "Phylis", + "Phyllis", + "Prisca", + "Priscah", + "Priscilla", + "Priscillah", + "Prudence", + "Purity", + "Quinter", + "Rabecca", + "Rachael", + "Racheal", + "Rachel", + "Rael", + "Rahab", + "Rahma", + "Rebecca", + "Regina", + "Rehema", + "Rhoda", + "Risper", + "Rita", + "Rodah", + "Rose", + "Roseline", + "Roselyne", + "Rosemary", + "Rukia", + "Ruth", + "Sabina", + "Saida", + "Sally", + "Salma", + "Salome", + "Sandra", + "Sara", + "Sarah", + "Saumu", + "Selina", + "Serah", + "Sharon", + "Sheila", + "Sheilah", + "Sheilla", + "Sheillah", + "Silvia", + "Sofia", + "Sophia", + "Sophy", + "Stacy", + "Stella", + "Stellah", + "Susan", + "Sylivia", + "Sylvia", + "Tabitha", + "Teresa", + "Teresia", + "Teresiah", + "Tracy", + "Valary", + "Vallary", + "Velma", + "Veronica", + "Veronicah", + "Vicky", + "Victoria", + "Viola", + "Violet", + "Virginia", + "Vivian", + "Weddy", + "Wendy", + "Winfred", + "Winnie", + "Winny", + "Yvonne", + "Zainab", + "Zainabu", + "Zipporah" ]; /** @@ -359,7 +1171,6 @@ class Person extends \Faker\Provider\Person "James", "Abdirahman", "Bakari", - "Mutwiri", "Gakii", "Omollo", @@ -860,8 +1671,6 @@ class Person extends \Faker\Provider\Person "Bor", "Galgalo", "Mbiti", - - "Mauti", "Oginga", "Dida", @@ -1111,7 +1920,7 @@ class Person extends \Faker\Provider\Person "Omweri", "Maghanga", "Nafuna", - "Angwenyi", + "Angwenyi" ]; /** From 77086aaa88c6f0bb20f715025e1668f643704de3 Mon Sep 17 00:00:00 2001 From: titustum Date: Sun, 24 Aug 2025 17:32:30 +0300 Subject: [PATCH 5/8] Enhance Kenyan Faker providers by adding references to external sources in Address, Company, and Person classes --- src/Provider/en_KE/Address.php | 6 ++++-- src/Provider/en_KE/Company.php | 1 + src/Provider/en_KE/Person.php | 13 +++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Provider/en_KE/Address.php b/src/Provider/en_KE/Address.php index ab0c67e128..9937aaab1b 100644 --- a/src/Provider/en_KE/Address.php +++ b/src/Provider/en_KE/Address.php @@ -8,11 +8,12 @@ * Provides Kenyan specific address data for Faker. * Includes cities, counties, streets, and regions commonly used in Kenya. * Extends the base Faker Address provider. + * See https://en.wikipedia.org/wiki/Provinces_of_Kenya */ class Address extends \Faker\Provider\Address { /** - * @var array List of major cities, municipalities, county headquarters, and local towns in Kenya. + * @var array List of major cities, municipalities, county headquarters, and local towns in Kenya. */ protected static $city = [ 'Nairobi', @@ -118,7 +119,7 @@ class Address extends \Faker\Provider\Address ]; /** - * @var array List of all Kenyan counties. + * @var array List of all Kenyan counties. */ protected static $county = [ 'Mombasa', @@ -241,6 +242,7 @@ class Address extends \Faker\Provider\Address /** * @var array List of Kenya's broad geographical regions. + * @see https://en.wikipedia.org/wiki/Provinces_of_Kenya */ protected static $region = [ 'Nairobi', diff --git a/src/Provider/en_KE/Company.php b/src/Provider/en_KE/Company.php index 77cbed7599..e832aeceac 100644 --- a/src/Provider/en_KE/Company.php +++ b/src/Provider/en_KE/Company.php @@ -13,6 +13,7 @@ class Company extends \Faker\Provider\Company { /** * @var array List of Kenyan company names. + * See https://en.wikipedia.org/wiki/List_of_companies_of_Kenya */ protected static $companyName = [ 'Jumia Kenya', diff --git a/src/Provider/en_KE/Person.php b/src/Provider/en_KE/Person.php index ff3496f0e0..6242252f71 100644 --- a/src/Provider/en_KE/Person.php +++ b/src/Provider/en_KE/Person.php @@ -2,6 +2,7 @@ namespace Faker\Provider\en_KE; + /** * Class Person * @@ -9,12 +10,15 @@ * Includes male and female first names representing both Christian and Islamic communities. * Also provides Kenyan surnames sourced from forebears.io. * Extends the base Faker Person provider. + * + * @see https://forebears.io/kenya/forenames + * @see https://forebears.io/kenya/surnames */ + class Person extends \Faker\Provider\Person { /** * @var array Kenyan male first names. - * Source: https://forebears.io/kenya/forenames */ protected static $firstNameMale = [ "Aaron", @@ -575,8 +579,7 @@ class Person extends \Faker\Provider\Person ]; /** - * @var array Kenyan female first names. - * Source: https://forebears.io/kenya/forenames + * @var array Kenyan female first names. */ protected static $firstNameFemale = [ "Abigael", @@ -916,9 +919,7 @@ class Person extends \Faker\Provider\Person ]; /** - * @var array Kenyan surnames. - * - * Source: https://forebears.io/kenya/surnames + * @var array Kenyan surnames. */ protected static $lastName = [ "Otieno", From e112f17fb65c6ce5ae9b50a26808633ba8f1fdbb Mon Sep 17 00:00:00 2001 From: titustum Date: Sat, 6 Sep 2025 10:38:25 +0300 Subject: [PATCH 6/8] Updated composer to support KE --- composer.json | 8 ++++---- src/Factory.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 0cacb0080a..f5679de575 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { - "name": "fakerphp/faker", - "type": "library", - "description": "Faker is a PHP library that generates fake data for you.", + "name": "titustum/faker", // 👈 Your unique package name + "description": "Faker PHP with Kenyan (en_KE) locale support", + "type": "library", "keywords": [ "faker", "fixtures", @@ -10,7 +10,7 @@ "license": "MIT", "authors": [ { - "name": "François Zaninotto" + "name": "Titus Tum" } ], "require": { diff --git a/src/Factory.php b/src/Factory.php index e9d201432d..a40be985b4 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -4,7 +4,7 @@ class Factory { - public const DEFAULT_LOCALE = 'en_US'; + public const DEFAULT_LOCALE = 'en_KE'; protected static $defaultProviders = ['Address', 'Barcode', 'Biased', 'Color', 'Company', 'DateTime', 'File', 'HtmlLorem', 'Image', 'Internet', 'Lorem', 'Medical', 'Miscellaneous', 'Payment', 'Person', 'PhoneNumber', 'Text', 'UserAgent', 'Uuid']; From 3ba88adf4a071991e327bea35001b2d9fb386ce0 Mon Sep 17 00:00:00 2001 From: titustum Date: Sat, 6 Sep 2025 10:38:59 +0300 Subject: [PATCH 7/8] Better composer --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f5679de575..6f873876fb 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "titustum/faker", // 👈 Your unique package name + "name": "titustum/faker", "description": "Faker PHP with Kenyan (en_KE) locale support", "type": "library", "keywords": [ From 05a0b7d7677ab1ebd713c2fad8d308134ab69aef Mon Sep 17 00:00:00 2001 From: titustum Date: Sat, 6 Sep 2025 10:48:01 +0300 Subject: [PATCH 8/8] ** --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6f873876fb..bbaa079709 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "forward-command": false }, "branch-alias": { - "dev-main": "v1.21-dev" + "dev-main": "v1.0-dev" } } }