diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml
new file mode 100644
index 0000000..c6f9107
--- /dev/null
+++ b/.github/workflows/compatibility.yml
@@ -0,0 +1,37 @@
+name: PHP Compatibility
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ php-compatibility:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.1'
+ extensions: dom, curl, libxml, mbstring, zip
+ coverage: none
+
+ - name: Install dependencies
+ run: |
+ composer update --prefer-dist --no-interaction
+ composer require --dev phpcompatibility/php-compatibility
+
+ - name: Install PHPCS
+ run: |
+ composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
+ composer require --dev dealerdirect/phpcodesniffer-composer-installer
+
+ - name: Check PHP compatibility
+ run: |
+ vendor/bin/phpcs --config-set installed_paths vendor/phpcompatibility/php-compatibility
+ vendor/bin/phpcs -p src/ --standard=PHPCompatibility --runtime-set testVersion 8.1-
\ No newline at end of file
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..ea70d2e
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,42 @@
+name: Tests
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: true
+ matrix:
+ php: [8.1, 8.2, 8.3, '8.4']
+ stability: [prefer-stable]
+
+ name: PHP ${{ matrix.php }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: dom, curl, libxml, mbstring, zip
+ coverage: none
+
+ - name: Install dependencies
+ run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
+
+ - name: Execute tests
+ run: vendor/bin/phpunit
+
+ - name: Check coding standards
+ run: vendor/bin/phpcs --standard=PSR12 -n src/ tests/
+
+ - name: Static analysis
+ run: vendor/bin/phpstan analyse
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 6800149..9e8c0b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
/vendor/
composer.lock
-.idea/
\ No newline at end of file
+.idea/.phpunit.cache/
diff --git a/.travis.yml b/.travis.yml
index 25dbd82..60bca3a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,16 @@
language: php
php:
- - "7.3"
- - "7.2"
- - "7.1"
+ - "8.1"
+ - "8.2"
+ - "8.3"
+ - "8.4"
install: composer install
script:
- vendor/bin/phpunit
- - vendor/bin/phpstan.phar analyse -l max -c phpstan.neon src --no-interaction --no-progress
-
+ - vendor/bin/phpstan analyse
+ - vendor/bin/phpcs --standard=PSR12 src/ tests/
notifications:
recipients:
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..743aaf0
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,27 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [2.0.0] - 2025-05-14
+### Added
+- Support for PHP 8.1, 8.2, 8.3, 8.4
+- GitHub Actions workflows for CI
+- Improved type declarations with PHPStan annotations
+- Enhanced documentation and examples
+- PHPStan level 8 compliance
+- PSR-12 code style
+
+### Changed
+- Modernized code style to follow PSR-12
+- Improved null safety with proper null checks
+- Refactored to use modern PHP features
+- Updated constructor parameter types for better type safety
+
+### Removed
+- Support for PHP 7.x and 8.0
+- Removed Travis CI in favor of GitHub Actions
+
+## [1.0.0] - Previous version
+- Initial release with PHP 7.1+ support
\ No newline at end of file
diff --git a/README.md b/README.md
index f35b619..b35b334 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-
+
------
-*Note*: The 1.0 release requires PHP > 7.1.
+*Note*: Version 2.0 requires PHP >= 8.1. For PHP 7.1-8.0 support, use version 1.x.
# Description
Fork from HumanNameParser.php origninally by Jason Priem . Takes human names of arbitrary complexity and various wacky formats like:
@@ -28,6 +28,16 @@ and parses out the:
- title (like 'Dr.', 'Prof') *new*
+# Requirements
+
+- PHP 8.1, 8.2, 8.3, 8.4 or higher
+
+# Installation
+
+```bash
+composer require davidgorges/human-name-parser
+```
+
# How to use
```php
@@ -36,5 +46,94 @@ use HumanNameParser\Parser;
$nameparser = new Parser();
$name = $nameparser->parse("Alfonso Ribeiro");
-echo "Hello " . $name->getFirstName();
+echo "Hello " . $name->getFirstName(); // "Hello Alfonso"
+```
+
+## More Examples
+
+```php
+// Basic usage
+$parser = new Parser();
+
+// Simple name
+$name = $parser->parse("John Smith");
+echo $name->getFirstName(); // "John"
+echo $name->getLastName(); // "Smith"
+
+// Name with title
+$name = $parser->parse("Dr. Jane Smith");
+echo $name->getAcademicTitle(); // "Dr."
+echo $name->getFirstName(); // "Jane"
+echo $name->getLastName(); // "Smith"
+
+// Name with middle name
+$name = $parser->parse("John William Smith");
+echo $name->getFirstName(); // "John"
+echo $name->getMiddleName(); // "William"
+echo $name->getLastName(); // "Smith"
+
+// Name with suffix
+$name = $parser->parse("John Smith Jr.");
+echo $name->getFirstName(); // "John"
+echo $name->getLastName(); // "Smith"
+echo $name->getSuffix(); // "Jr."
+
+// Reversed name (last, first)
+$name = $parser->parse("Smith, John");
+echo $name->getFirstName(); // "John"
+echo $name->getLastName(); // "Smith"
+
+// Complex name
+$name = $parser->parse("Dr. John W. ('Johnny') Smith-Brown, III");
+echo $name->getAcademicTitle(); // "Dr."
+echo $name->getFirstName(); // "John"
+echo $name->getMiddleName(); // "W."
+echo $name->getNicknames(); // "Johnny"
+echo $name->getLastName(); // "Smith-Brown"
+echo $name->getSuffix(); // "III"
+```
+
+## Custom Configuration
+
+```php
+// Configure the parser with custom options
+$parser = new Parser([
+ 'suffixes' => ['esq', 'jr', 'sr', 'ii', 'iii', 'iv'],
+ 'prefixes' => ['van', 'von', 'de', 'del', 'da', 'la'],
+ 'academic_titles' => ['dr', 'prof', 'mr', 'mrs', 'ms'],
+ 'mandatory_first_name' => true,
+ 'mandatory_last_name' => true
+]);
+
+// Parse name with prefix
+$name = $parser->parse("Vincent van Gogh");
+echo $name->getFirstName(); // "Vincent"
+echo $name->getLastName(); // "van Gogh"
+```
+
+# Try It Out
+
+The library includes an example script that you can use to test parsing various names:
+
+```bash
+# After installing the library
+composer install
+php examples/parse_name.php
+
+# Or parse a specific name directly
+php examples/parse_name.php "Dr. John Smith Jr."
+```
+
+# Testing
+
+```bash
+composer install
+vendor/bin/phpunit
+```
+
+# Static Analysis & Coding Standards
+
+```bash
+vendor/bin/phpstan analyse
+vendor/bin/phpcs --standard=PSR12 src/ tests/
```
diff --git a/composer.json b/composer.json
index d6404de..995d710 100644
--- a/composer.json
+++ b/composer.json
@@ -13,11 +13,12 @@
}
],
"require": {
- "php": ">=7.1"
+ "php": ">=8.1"
},
"require-dev": {
- "phpunit/phpunit": "7.*",
- "phpstan/phpstan-shim": "^0.12.0"
+ "phpunit/phpunit": "^10.0",
+ "phpstan/phpstan": "^1.10",
+ "squizlabs/php_codesniffer": "^3.7"
},
"autoload": {
"psr-4": {"HumanNameParser\\": "src/HumanNameParser"}
diff --git a/examples/parse_name.php b/examples/parse_name.php
new file mode 100644
index 0000000..a395b5a
--- /dev/null
+++ b/examples/parse_name.php
@@ -0,0 +1,66 @@
+ 1) {
+ // Get the name from the command-line arguments
+ $nameString = implode(' ', array_slice($argv, 1));
+} else {
+ // Example names to parse
+ $examples = [
+ "John Smith",
+ "Dr. Jane Smith",
+ "Smith, John",
+ "John William Smith",
+ "John Smith Jr.",
+ "Dr. John W. ('Johnny') Smith-Brown, III",
+ "Vincent van Gogh",
+ "James C. O'Neill",
+ "Björn O'Malley, Jr."
+ ];
+
+ // Let user choose an example or enter their own name
+ echo "Choose an example to parse:\n";
+ foreach ($examples as $i => $example) {
+ echo ($i + 1) . ". $example\n";
+ }
+ echo ($i + 2) . ". Enter your own name\n";
+ echo "Your choice (1-" . ($i + 2) . "): ";
+
+ $choice = (int) trim(fgets(STDIN));
+
+ if ($choice === count($examples) + 1) {
+ echo "Enter a name to parse: ";
+ $nameString = trim(fgets(STDIN));
+ } elseif ($choice > 0 && $choice <= count($examples)) {
+ $nameString = $examples[$choice - 1];
+ } else {
+ echo "Invalid choice. Using first example.\n";
+ $nameString = $examples[0];
+ }
+}
+
+// Create a new Parser instance
+$parser = new Parser();
+
+try {
+ // Parse the name
+ $name = $parser->parse($nameString);
+
+ // Display the results
+ echo "\nParsing results for: " . $nameString . "\n";
+ echo str_repeat("-", 40) . "\n";
+ echo "Academic Title: " . ($name->getAcademicTitle() ?? "N/A") . "\n";
+ echo "Leading Initial: " . ($name->getLeadingInitial() ?? "N/A") . "\n";
+ echo "First Name: " . ($name->getFirstName() ?? "N/A") . "\n";
+ echo "Nicknames: " . ($name->getNicknames() ?? "N/A") . "\n";
+ echo "Middle Name: " . ($name->getMiddleName() ?? "N/A") . "\n";
+ echo "Last Name: " . ($name->getLastName() ?? "N/A") . "\n";
+ echo "Suffix: " . ($name->getSuffix() ?? "N/A") . "\n";
+
+} catch (Exception $e) {
+ echo "Error parsing name: " . $e->getMessage() . "\n";
+}
\ No newline at end of file
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..5ddce83
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,12 @@
+
+
+ PSR-12 coding standard
+
+
+
+
+ src
+ tests
+
+
+
\ No newline at end of file
diff --git a/phpstan.neon b/phpstan.neon
index e69de29..49cb743 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -0,0 +1,5 @@
+parameters:
+ level: 8
+ paths:
+ - src
+ - tests
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index 5952c58..eab8ca9 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,10 +1,18 @@
+ bootstrap="tests/bootstrap.php"
+ executionOrder="random"
+ cacheDirectory=".phpunit.cache">
- Tests
+ tests
+
+
+
+
\ No newline at end of file
diff --git a/src/HumanNameParser/Exception/FirstNameNotFoundException.php b/src/HumanNameParser/Exception/FirstNameNotFoundException.php
index 6588b1d..25e53fb 100644
--- a/src/HumanNameParser/Exception/FirstNameNotFoundException.php
+++ b/src/HumanNameParser/Exception/FirstNameNotFoundException.php
@@ -3,8 +3,8 @@
namespace HumanNameParser\Exception;
/**
-* Exception first name not found
-*/
+ * Exception first name not found
+ */
class FirstNameNotFoundException extends NameParsingException
{
-}
\ No newline at end of file
+}
diff --git a/src/HumanNameParser/Exception/LastNameNotFoundException.php b/src/HumanNameParser/Exception/LastNameNotFoundException.php
index 3cfd267..1d426bf 100644
--- a/src/HumanNameParser/Exception/LastNameNotFoundException.php
+++ b/src/HumanNameParser/Exception/LastNameNotFoundException.php
@@ -3,8 +3,8 @@
namespace HumanNameParser\Exception;
/**
-* Exception last name not found
-*/
+ * Exception last name not found
+ */
class LastNameNotFoundException extends NameParsingException
{
-}
\ No newline at end of file
+}
diff --git a/src/HumanNameParser/Exception/NameParsingException.php b/src/HumanNameParser/Exception/NameParsingException.php
index 7c480fe..ce7a616 100644
--- a/src/HumanNameParser/Exception/NameParsingException.php
+++ b/src/HumanNameParser/Exception/NameParsingException.php
@@ -3,8 +3,8 @@
namespace HumanNameParser\Exception;
/**
-* Exception for NameParsing
-*/
+ * Exception for NameParsing
+ */
class NameParsingException extends \Exception
{
-}
\ No newline at end of file
+}
diff --git a/src/HumanNameParser/Name.php b/src/HumanNameParser/Name.php
index 072cde0..463af83 100644
--- a/src/HumanNameParser/Name.php
+++ b/src/HumanNameParser/Name.php
@@ -2,53 +2,53 @@
namespace HumanNameParser;
-class Name {
-
+class Name
+{
/**
- * @var string
+ * @var string|null
*/
- private $leadingInitial;
+ private $leadingInitial = null;
/**
- * @var string
+ * @var string|null
*/
- private $firstName;
+ private $firstName = null;
/**
- * @var string
+ * @var string|null
*/
- private $nicknames;
+ private $nicknames = null;
/**
- * @var string
+ * @var string|null
*/
- private $middleName;
+ private $middleName = null;
/**
- * @var string
+ * @var string|null
*/
- private $lastName;
+ private $lastName = null;
/**
- * @var string
+ * @var string|null
*/
- private $academicTitle;
+ private $academicTitle = null;
/**
- * @var string
+ * @var string|null
*/
- private $suffix;
+ private $suffix = null;
/**
* Gets the value of firstName.
*
* @return string
*/
- public function getFirstName()
+ public function getFirstName(): ?string
{
return $this->firstName;
}
-
+
/**
* Sets the value of firstName.
*
@@ -56,7 +56,7 @@ public function getFirstName()
*
* @return self
*/
- public function setFirstName($firstName)
+ public function setFirstName(?string $firstName): self
{
$this->firstName = $firstName;
@@ -68,11 +68,11 @@ public function setFirstName($firstName)
*
* @return string
*/
- public function getNicknames()
+ public function getNicknames(): ?string
{
return $this->nicknames;
}
-
+
/**
* Sets the value of nicknames.
*
@@ -80,7 +80,7 @@ public function getNicknames()
*
* @return self
*/
- public function setNicknames($nicknames)
+ public function setNicknames(?string $nicknames): self
{
$this->nicknames = $nicknames;
@@ -92,11 +92,11 @@ public function setNicknames($nicknames)
*
* @return string
*/
- public function getMiddleName()
+ public function getMiddleName(): ?string
{
return $this->middleName;
}
-
+
/**
* Sets the value of middleName.
*
@@ -104,7 +104,7 @@ public function getMiddleName()
*
* @return self
*/
- public function setMiddleName($middleName)
+ public function setMiddleName(?string $middleName): self
{
$this->middleName = $middleName;
@@ -116,11 +116,11 @@ public function setMiddleName($middleName)
*
* @return string
*/
- public function getLastName()
+ public function getLastName(): ?string
{
return $this->lastName;
}
-
+
/**
* Sets the value of lastName.
*
@@ -128,7 +128,7 @@ public function getLastName()
*
* @return self
*/
- public function setLastName($lastName)
+ public function setLastName(?string $lastName): self
{
$this->lastName = $lastName;
@@ -140,11 +140,11 @@ public function setLastName($lastName)
*
* @return string
*/
- public function getSuffix()
+ public function getSuffix(): ?string
{
return $this->suffix;
}
-
+
/**
* Sets the value of suffix.
*
@@ -152,7 +152,7 @@ public function getSuffix()
*
* @return self
*/
- public function setSuffix($suffix)
+ public function setSuffix(?string $suffix): self
{
$this->suffix = $suffix;
@@ -164,11 +164,11 @@ public function setSuffix($suffix)
*
* @return string
*/
- public function getLeadingInitial()
+ public function getLeadingInitial(): ?string
{
return $this->leadingInitial;
}
-
+
/**
* Sets the value of leadingInitial.
*
@@ -176,7 +176,7 @@ public function getLeadingInitial()
*
* @return self
*/
- public function setLeadingInitial($leadingInitial)
+ public function setLeadingInitial(?string $leadingInitial): self
{
$this->leadingInitial = $leadingInitial;
@@ -188,11 +188,11 @@ public function setLeadingInitial($leadingInitial)
*
* @return string
*/
- public function getAcademicTitle()
+ public function getAcademicTitle(): ?string
{
return $this->academicTitle;
}
-
+
/**
* Sets the value of academicTitle.
*
@@ -200,10 +200,10 @@ public function getAcademicTitle()
*
* @return self
*/
- public function setAcademicTitle($academicTitle)
+ public function setAcademicTitle(?string $academicTitle): self
{
$this->academicTitle = $academicTitle;
return $this;
}
-}
\ No newline at end of file
+}
diff --git a/src/HumanNameParser/Parser.php b/src/HumanNameParser/Parser.php
index b9f9b2c..6dfcbdc 100644
--- a/src/HumanNameParser/Parser.php
+++ b/src/HumanNameParser/Parser.php
@@ -13,44 +13,46 @@
class Parser
{
-
// The regex use is a bit tricky. *Everything* matched by the regex will be replaced,
// but you can select a particular parenthesized submatch to be returned.
// Also, note that each regex requires that the preceding ones have been run, and matches chopped out.
- CONST REGEX_NICKNAMES = "/ ('|\"|\(\"*'*)(.+?)('|\"|\"*'*\)) /i"; // names that starts or end w/ an apostrophe break this
- CONST REGEX_TITLES = "/^(%s)\.*/i";
- CONST REGEX_SUFFIX = "/(\*,) *(%s)$/i";
- CONST REGEX_LAST_NAME = "/(?!^)\b([^ ]+ y |%s)*[^ ]+$/i";
- CONST REGEX_LEADING_INITIAL = "/^(.\.*)(?= \p{L}{2})/i"; // note the lookahead, which isn't returned or replaced
- CONST REGEX_FIRST_NAME = "/^[^ ]+/i"; //
+ // Names that starts or end with an apostrophe break this
+ private const REGEX_NICKNAMES = "/ ('|\"|\(\"*'*)(.+?)('|\"|\"*'*\)) /i";
+ private const REGEX_TITLES = "/^(%s)\.*/i";
+ // Using a different approach instead of this regex
+ // private const REGEX_SUFFIX = "/(\*,) *(%s)$/i";
+ private const REGEX_LAST_NAME = "/(?!^)\b([^ ]+ y |%s)*[^ ]+$/i";
+ // Note the lookahead, which isn't returned or replaced
+ private const REGEX_LEADING_INITIAL = "/^(.\.*)(?= \p{L}{2})/i";
+ private const REGEX_FIRST_NAME = "/^[^ ]+/i"; //
/**
- * @var array
+ * @var array
*/
- private $suffixes = array();
+ private $suffixes = [];
/**
- * @var array
+ * @var array
*/
- private $prefixes = array();
+ private $prefixes = [];
/**
- * @var array
+ * @var array
*/
- private $academicTitles = array();
+ private $academicTitles = [];
/**
- * @var string
+ * @var string|null
*/
private $nameToken = null;
/**
- * @var boolean
+ * @var bool
*/
private $mandatoryFirstName = true;
/**
- * @var boolean
+ * @var bool
*/
private $mandatoryLastName = true;
@@ -59,30 +61,36 @@ class Parser
*/
private $name;
- /*
+ /**
* Constructor
*
- * @param array of options
- * 'suffixes' for an array of suffixes
- * 'prefix' for an array of prefixes
+ * Options:
+ * - 'suffixes' - Array of name suffixes
+ * - 'prefixes' - Array of name prefixes
+ * - 'academic_titles' - Array of academic titles
+ * - 'mandatory_first_name' - If true, requires first name (default: true)
+ * - 'mandatory_last_name' - If true, requires last name (default: true)
+ */
+ /**
+ * @param array $options
*/
- public function __construct($options = array())
+ public function __construct(array $options = [])
{
if (!isset($options['suffixes'])) {
- $options['suffixes'] = array('esq', 'esquire', 'jr', 'sr', '2', 'ii', 'iii', 'iv');
+ $options['suffixes'] = ['esq', 'esquire', 'jr', 'sr', '2', 'ii', 'iii', 'iv'];
}
if (!isset($options['prefixes'])) {
- $options['prefixes'] = array('bar', 'ben', 'bin', 'da', 'dal', 'de la', 'de', 'del', 'der', 'di',
- 'ibn', 'la', 'le', 'san', 'st', 'ste', 'van', 'van der', 'van den', 'vel', 'von');
+ $options['prefixes'] = ['bar', 'ben', 'bin', 'da', 'dal', 'de la', 'de', 'del', 'der', 'di',
+ 'ibn', 'la', 'le', 'san', 'st', 'ste', 'van', 'van der', 'van den', 'vel', 'von'];
}
if (!isset($options['academic_titles'])) {
- $options['academic_titles'] = array('ms', 'miss', 'mrs', 'mr', 'prof', 'dr');
+ $options['academic_titles'] = ['ms', 'miss', 'mrs', 'mr', 'prof', 'dr'];
}
if (isset($options['mandatory_first_name'])) {
- $this->mandatoryFirstName = (boolean)$options['mandatory_first_name'];
+ $this->mandatoryFirstName = (bool)$options['mandatory_first_name'];
}
if (isset($options['mandatory_last_name'])) {
- $this->mandatoryLastName = (boolean)$options['mandatory_last_name'];
+ $this->mandatoryLastName = (bool)$options['mandatory_last_name'];
}
$this->name = new Name();
@@ -98,7 +106,7 @@ public function __construct($options = array())
*
* @return Name the parsed name
*/
- public function parse($name)
+ public function parse(string $name): Name
{
$suffixes = implode("\.*|", $this->suffixes) . "\.*"; // each suffix gets a "\.*" behind it.
$prefixes = implode(" |", $this->prefixes) . " "; // each prefix gets a " " behind it.
@@ -131,13 +139,15 @@ public function parse($name)
*
* @return Parser
*/
- private function findAcademicTitle($academicTitles)
+ private function findAcademicTitle(string $academicTitles): self
{
$regex = sprintf(self::REGEX_TITLES, $academicTitles);
$title = $this->findWithRegex($regex, 1);
if ($title) {
$this->name->setAcademicTitle($title);
- $this->nameToken = str_ireplace($title, "", $this->nameToken);
+ if ($this->nameToken !== null) {
+ $this->nameToken = str_ireplace($title, "", $this->nameToken);
+ }
}
return $this;
@@ -147,7 +157,7 @@ private function findAcademicTitle($academicTitles)
/**
* @return Parser
*/
- private function findNicknames()
+ private function findNicknames(): self
{
$nicknames = $this->findWithRegex(self::REGEX_NICKNAMES, 2);
if ($nicknames) {
@@ -163,7 +173,7 @@ private function findNicknames()
*
* @return Parser
*/
- private function findSuffix($suffixes)
+ private function findSuffix(string $suffixes): self
{
$regex = "/,* *($suffixes)$/i";
//var_dump($regex); die;
@@ -180,7 +190,7 @@ private function findSuffix($suffixes)
/**
* @return Parser
*/
- private function findLastName($prefixes)
+ private function findLastName(string $prefixes): self
{
$regex = sprintf(self::REGEX_LAST_NAME, $prefixes);
$lastName = $this->findWithRegex($regex, 0);
@@ -188,7 +198,6 @@ private function findLastName($prefixes)
$this->name->setLastName($lastName);
$this->removeTokenWithRegex($regex);
} elseif ($this->mandatoryLastName) {
-
throw new LastNameNotFoundException("Couldn't find a last name.");
}
@@ -198,14 +207,13 @@ private function findLastName($prefixes)
/**
* @return Parser
*/
- private function findFirstName()
+ private function findFirstName(): self
{
$lastName = $this->findWithRegex(self::REGEX_FIRST_NAME, 0);
if ($lastName) {
$this->name->setFirstName($lastName);
$this->removeTokenWithRegex(self::REGEX_FIRST_NAME);
} elseif ($this->mandatoryFirstName) {
-
throw new FirstNameNotFoundException("Couldn't find a first name.");
}
@@ -215,7 +223,7 @@ private function findFirstName()
/**
* @return Parser
*/
- private function findLeadingInitial()
+ private function findLeadingInitial(): self
{
$leadingInitial = $this->findWithRegex(self::REGEX_LEADING_INITIAL, 1);
if ($leadingInitial) {
@@ -229,9 +237,9 @@ private function findLeadingInitial()
/**
* @return Parser
*/
- private function findMiddleName()
+ private function findMiddleName(): self
{
- $middleName = trim($this->nameToken);
+ $middleName = $this->nameToken !== null ? trim($this->nameToken) : '';
if ($middleName) {
$this->name->setMiddleName($middleName);
}
@@ -243,9 +251,12 @@ private function findMiddleName()
/**
* @return string
*/
- private function findWithRegex($regex, $submatchIndex = 0)
+ private function findWithRegex(string $regex, int $submatchIndex = 0): string|false
{
$regex = $regex . "ui"; // unicode + case-insensitive
+ if ($this->nameToken === null) {
+ return false;
+ }
preg_match($regex, $this->nameToken, $m);
$subset = (isset($m[$submatchIndex])) ? $m[$submatchIndex] : false;
@@ -259,8 +270,12 @@ private function findWithRegex($regex, $submatchIndex = 0)
* @return void
* @throws NameParsingException
*/
- private function removeTokenWithRegex($regex)
+ private function removeTokenWithRegex(string $regex): void
{
+ if ($this->nameToken === null) {
+ return;
+ }
+
$numReplacements = 0;
$tokenRemoved = preg_replace($regex, ' ', $this->nameToken, -1, $numReplacements);
if ($numReplacements > 1) {
@@ -281,17 +296,29 @@ private function removeTokenWithRegex($regex)
*
* @return string
*/
- private function normalize($taintedString)
+ private function normalize(string $taintedString): string
{
- if (!is_string($taintedString)) {
- throw new \InvalidArgumentException('Parameter is expected to be a string.');
+ $result = preg_replace("#^\s*#u", '', $taintedString);
+ if ($result === null) {
+ return '';
+ }
+
+ $result = preg_replace("#\s*$#u", '', $result);
+ if ($result === null) {
+ return '';
+ }
+
+ $result = preg_replace("#\s+#u", ' ', $result);
+ if ($result === null) {
+ return '';
+ }
+
+ $result = preg_replace('#,$#u', ' ', $result);
+ if ($result === null) {
+ return '';
}
- $taintedString = preg_replace("#^\s*#u", '', (string) $taintedString);
- $taintedString = preg_replace("#\s*$#u", '', (string) $taintedString);
- $taintedString = preg_replace("#\s+#u", ' ', (string) $taintedString);
- $taintedString = preg_replace('#,$#u', ' ', (string) $taintedString);
- return (string) $taintedString;
+ return $result;
}
/**
@@ -301,9 +328,11 @@ private function normalize($taintedString)
*
* @throws NameParsingException
*/
- private function flipNameToken($pattern = ",")
+ private function flipNameToken(string $pattern = ","): self
{
- $this->nameToken = $this->flipStringPartsAround($this->nameToken, $pattern);
+ if ($this->nameToken !== null) {
+ $this->nameToken = $this->flipStringPartsAround($this->nameToken, $pattern);
+ }
return $this;
}
@@ -320,18 +349,17 @@ private function flipNameToken($pattern = ",")
* @return string
* @throws NameParsingException
*/
- private function flipStringPartsAround($string, $char)
+ private function flipStringPartsAround(string $string, string $char): string
{
- $substrings = preg_split("/$char/u", (string) $string);
- if(!is_array($substrings)) {
+ $substrings = preg_split("/$char/u", $string);
+ if (!is_array($substrings)) {
throw new NameParsingException('Could not flip characters.');
}
if (\count($substrings) === 2) {
$string = $substrings[1] . ' ' . $substrings[0];
$string = $this->normalize($string);
- } else if (\count($substrings) > 2) {
-
+ } elseif (\count($substrings) > 2) {
throw new NameParsingException("Can't flip around multiple '$char' characters in namestring.");
}
@@ -343,7 +371,10 @@ private function flipStringPartsAround($string, $char)
*
* @return array
*/
- public function getSuffixes()
+ /**
+ * @return array
+ */
+ public function getSuffixes(): array
{
return $this->suffixes;
}
@@ -355,7 +386,10 @@ public function getSuffixes()
*
* @return self
*/
- public function setSuffixes(array $suffixes)
+ /**
+ * @param array $suffixes
+ */
+ public function setSuffixes(array $suffixes): self
{
$this->suffixes = $suffixes;
@@ -367,7 +401,10 @@ public function setSuffixes(array $suffixes)
*
* @return array
*/
- public function getPrefixes()
+ /**
+ * @return array
+ */
+ public function getPrefixes(): array
{
return $this->prefixes;
}
@@ -379,7 +416,10 @@ public function getPrefixes()
*
* @return self
*/
- public function setPrefixes(array $prefixes)
+ /**
+ * @param array $prefixes
+ */
+ public function setPrefixes(array $prefixes): self
{
$this->prefixes = $prefixes;
@@ -391,7 +431,10 @@ public function setPrefixes(array $prefixes)
*
* @return array
*/
- public function getAcademicTitles()
+ /**
+ * @return array
+ */
+ public function getAcademicTitles(): array
{
return $this->academicTitles;
}
@@ -403,7 +446,10 @@ public function getAcademicTitles()
*
* @return self
*/
- public function setAcademicTitles(array $academicTitles)
+ /**
+ * @param array $academicTitles
+ */
+ public function setAcademicTitles(array $academicTitles): self
{
$this->academicTitles = $academicTitles;
diff --git a/tests/ParserTest.php b/tests/ParserTest.php
index 05826d8..fc65067 100644
--- a/tests/ParserTest.php
+++ b/tests/ParserTest.php
@@ -7,18 +7,17 @@
class ParserTest extends TestCase
{
-
/**
* @var Parser
*/
private $parser;
- public function setUp()
+ protected function setUp(): void
{
$this->parser = new Parser();
}
- public function testSuffix()
+ public function testSuffix(): void
{
$name = 'Björn O\'Malley, Jr.';
$nameObject = $this->parser->parse($name);
@@ -27,7 +26,7 @@ public function testSuffix()
$this->assertEquals('Jr.', $nameObject->getSuffix());
}
- public function testSimple()
+ public function testSimple(): void
{
$name = 'Hans Meiser';
$nameObject = $this->parser->parse($name);
@@ -35,7 +34,7 @@ public function testSimple()
$this->assertEquals('Meiser', $nameObject->getLastName());
}
- public function testReverse()
+ public function testReverse(): void
{
$name = 'Meiser, Hans';
$nameObject = $this->parser->parse($name);
@@ -43,7 +42,7 @@ public function testReverse()
$this->assertEquals('Meiser', $nameObject->getLastName());
}
- public function testReverseWithSlash()
+ public function testReverseWithSlash(): void
{
$name = 'Smith / Joe';
$nameObject = $this->parser->parse($name);
@@ -51,7 +50,7 @@ public function testReverseWithSlash()
$this->assertEquals('Smith', $nameObject->getLastName());
}
- public function testReverseWithAcademicTitle()
+ public function testReverseWithAcademicTitle(): void
{
$name = 'Dr. Meiser, Hans';
$nameObject = $this->parser->parse($name);
@@ -60,7 +59,7 @@ public function testReverseWithAcademicTitle()
$this->assertEquals('Hans', $nameObject->getFirstName());
}
- public function testithAcademicTitle()
+ public function testithAcademicTitle(): void
{
$name = 'Dr. Hans Meiser';
$nameObject = $this->parser->parse($name);
@@ -69,7 +68,7 @@ public function testithAcademicTitle()
$this->assertEquals('Hans', $nameObject->getFirstName());
}
- public function testLastNameWithPrefix()
+ public function testLastNameWithPrefix(): void
{
$name = 'Björn van Olst';
$nameObject = $this->parser->parse($name);
@@ -77,73 +76,99 @@ public function testLastNameWithPrefix()
$this->assertEquals('Björn', $nameObject->getFirstName());
}
- public function testNoFirstNameDefaultException()
+ public function testNoFirstNameDefaultException(): void
{
$name = 'Mr. Hyde';
$this->expectException('HumanNameParser\Exception\FirstNameNotFoundException');
$this->parser->parse($name);
}
- public function testNoLastNameDefaultException()
+ public function testNoLastNameDefaultException(): void
{
$name = 'Edward';
$this->expectException('HumanNameParser\Exception\LastNameNotFoundException');
$this->parser->parse($name);
}
- public function testFirstNameNotMandatory()
+ public function testFirstNameNotMandatory(): void
{
- $this->parser = new Parser(array('mandatory_first_name' => false));
+ $this->parser = new Parser(['mandatory_first_name' => false]);
$name = 'Dr. Jekyll';
$nameObject = $this->parser->parse($name);
$this->assertEquals('Dr.', $nameObject->getAcademicTitle());
$this->assertEquals('Jekyll', $nameObject->getLastName());
}
- public function testLastNameNotMandatory()
+ public function testLastNameNotMandatory(): void
{
- $this->parser = new Parser(array('mandatory_last_name' => false));
+ $this->parser = new Parser(['mandatory_last_name' => false]);
$name = 'Henry';
$nameObject = $this->parser->parse($name);
$this->assertEquals('Henry', $nameObject->getFirstName());
}
- public function testFirstNameMandatory()
+ public function testFirstNameMandatory(): void
{
- $this->parser = new Parser(array('mandatory_first_name' => true));
+ $this->parser = new Parser(['mandatory_first_name' => true]);
$name = 'Mr. Hyde';
$this->expectException('HumanNameParser\Exception\FirstNameNotFoundException');
$this->parser->parse($name);
}
- public function testLastNameMandatory()
+ public function testLastNameMandatory(): void
{
- $this->parser = new Parser(array('mandatory_last_name' => true));
+ $this->parser = new Parser(['mandatory_last_name' => true]);
$name = 'Edward';
$this->expectException('HumanNameParser\Exception\LastNameNotFoundException');
$this->parser->parse($name);
}
- public function testNameList()
+ public function testNameList(): void
{
$names = $this->getNames();
foreach ($names as $nameStr) {
$nameparts = explode(';', $nameStr);
$name = $nameparts[0];
$nameObject = $this->parser->parse($name);
- $this->assertEquals($nameparts[1], $nameObject->getLeadingInitial(), sprintf("failed to ensure correct leading initial (%s) in name %s", $nameparts[1], $name));
- $this->assertEquals($nameparts[2], $nameObject->getFirstName(), sprintf("failed to ensure correct first name (%s) in name %s", $nameparts[2], $name));
- $this->assertEquals($nameparts[3], $nameObject->getNickNames(), sprintf("failed to ensure correct nickname (%s) in name %s", $nameparts[3], $name));
- $this->assertEquals($nameparts[4], $nameObject->getMiddleName(), sprintf("failed to ensure correct middle name (%s) in name %s", $nameparts[4], $name));
- $this->assertEquals($nameparts[5], $nameObject->getLastName(), sprintf("failed to ensure correct last name (%s) in name %s", $nameparts[5], $name));
- $this->assertEquals($nameparts[6], $nameObject->getSuffix(), sprintf("failed to ensure correct suffix (%s) in name %s", $nameparts[6], $name));
-
+ $this->assertEquals(
+ $nameparts[1],
+ $nameObject->getLeadingInitial(),
+ sprintf("failed to ensure correct leading initial (%s) in name %s", $nameparts[1], $name)
+ );
+ $this->assertEquals(
+ $nameparts[2],
+ $nameObject->getFirstName(),
+ sprintf("failed to ensure correct first name (%s) in name %s", $nameparts[2], $name)
+ );
+ $this->assertEquals(
+ $nameparts[3],
+ $nameObject->getNickNames(),
+ sprintf("failed to ensure correct nickname (%s) in name %s", $nameparts[3], $name)
+ );
+ $this->assertEquals(
+ $nameparts[4],
+ $nameObject->getMiddleName(),
+ sprintf("failed to ensure correct middle name (%s) in name %s", $nameparts[4], $name)
+ );
+ $this->assertEquals(
+ $nameparts[5],
+ $nameObject->getLastName(),
+ sprintf("failed to ensure correct last name (%s) in name %s", $nameparts[5], $name)
+ );
+ $this->assertEquals(
+ $nameparts[6],
+ $nameObject->getSuffix(),
+ sprintf("failed to ensure correct suffix (%s) in name %s", $nameparts[6], $name)
+ );
}
}
- private function getNames()
+ /**
+ * @return array
+ */
+ private function getNames(): array
{
- return array(
+ return [
'Björn O\'Malley;;Björn;;;O\'Malley;',
'Bin Lin;;Bin;;;Lin;',
'Linda Jones;;Linda;;;Jones;',
@@ -179,6 +204,6 @@ private function getNames()
'Smith / Joe;;Joe;;;Smith;',
'Smith/ Ms Jane Middle;;Jane;;Middle;Smith;',
'Smith Jr / Dr Joe;;Joe;;;Smith;Jr',
- );
+ ];
}
}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index d12d9cc..93f0e0f 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,15 +1,15 @@
add('HumanNameParser\\', __DIR__);
\ No newline at end of file
+$loader->add('HumanNameParser\\', __DIR__);
diff --git a/tests/helpers.php b/tests/helpers.php
new file mode 100644
index 0000000..7a6fb57
--- /dev/null
+++ b/tests/helpers.php
@@ -0,0 +1,13 @@
+