PHPackages                             iliaal/nameparser - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. iliaal/nameparser

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

iliaal/nameparser
=================

Parse a full-name string into its parts. Casing- and credential-aware fork of theiconic/name-parser, tuned for professional/clinician names.

v1.3.0(2w ago)8425MITPHPPHP ^8.3CI passing

Since Jun 7Pushed 1w ago1 watchersCompare

[ Source](https://github.com/iliaal/nameparser)[ Packagist](https://packagist.org/packages/iliaal/nameparser)[ RSS](/packages/iliaal-nameparser/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (4)Dependencies (12)Versions (5)Used By (0)

iliaal/nameparser
=================

[](#iliaalnameparser)

[![CI](https://github.com/iliaal/nameparser/actions/workflows/ci.yml/badge.svg)](https://github.com/iliaal/nameparser/actions/workflows/ci.yml)[![Latest Version](https://camo.githubusercontent.com/5931220536fcdaf4a0c44a08aa7ca1f26fd469bb70140829050bb4a6adbb37cc/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f696c6961616c2f6e616d65706172736572)](https://packagist.org/packages/iliaal/nameparser)[![PHP Version](https://camo.githubusercontent.com/da96775dcb0283101f8340ba50eee97b73740755dc3ece5e08d6a236da9af03a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f696c6961616c2f6e616d65706172736572)](https://packagist.org/packages/iliaal/nameparser)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](https://opensource.org/licenses/MIT)[![Follow @iliaa](https://camo.githubusercontent.com/a54521c97521f05fbadec4bd9bcba96ff1eeaffe756a6d7338b47a628cdeb39b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f466f6c6c6f772d40696c6961612d3030303030303f7374796c653d666c6174266c6f676f3d78266c6f676f436f6c6f723d7768697465)](https://x.com/intent/follow?screen_name=iliaa)

Parse a string containing a full name into its parts (salutation, first name, middle names, initials, last name with prefixes, suffix, nickname).

> **Fork lineage.** This is a fork of [theiconic/name-parser](https://github.com/theiconic/name-parser) (dormant since ~2020), built on top of the modernization done by [codebyzach/name-parser](https://github.com/CodeByZach/name-parser). It adds **casing- and credential-aware parsing** and a **confidence/ambiguity signal**, and targets PHP 8.3+.

Why this fork
-------------

[](#why-this-fork)

The upstream parser keys every token through `strtolower()` before matching it against its salutation/suffix dictionaries, so it cannot tell an all-caps credential from a same-spelled name. Two failure modes follow, both common in professional and clinician name lists:

1. A trailing credential without a comma swallows the surname: `"Jane Doe DDS"` parsed to last name **"Dds"** (the real surname lost).
2. A short credential token that is also a real name is mis-stripped: the Vietnamese surname **"Do"** and given name **"Vi"** were consumed as the credentials DO / VI.

This fork fixes both and adds an advisory confidence pass for the genuinely ambiguous cases.

### What changed

[](#what-changed)

- **Casing as a signal.** An ambiguous token (`Do`, `Vi`, `Ma`, roman numerals, two-letter credentials) is treated as a credential only when written ALL-CAPS (`DO`, `VI`); Title- or lower-case keeps it as a name part. People write credentials in caps and names in title case, so the original casing carries the signal that lowercasing discarded.
- **Terminal-token guard.** A lone name-colliding token in a comma given-name segment is kept as a name rather than emptied into a credential, unless its casing reads as a credential.
- **Confidence assessor.** When a token matches a credential but the casing is uninformative (uniform-case input, or a lowercase token), `Confidence::assess()`flags the input so you can route it to manual review instead of trusting the split.
- **Expanded English dictionary** (inherited from the CodeByZach fork): DDS, DO, DVM, PsyD, LCSW, MSW, MBA, EMBA, Esq, roman numerals VI to X, `Hon.`, and more.
- **Nursing and allied-health credentials.** RN, NP, PharmD, APRN, PA-C, OTR/L, and 30+ more, mined by frequency from the NPI registry, so a trailing credential no longer leaks into the first name.
- **Unclosed nickname delimiter.** An opening `(` or quote with no matching close no longer swallows the surname (`"John (Bob Smith"` keeps `Smith`).
- **All-caps short names.** Under uniform-uppercase input the caps cannot mark a token as initials, so a two-letter given name is kept as a name instead of being split (`"JO ANDERSON"` keeps `Jo`, not `J` + initial `O`). Mixed-case combined initials still split (`"JM Walker"` to `J` `M` Walker).
- **Comma middle names.** Everything after the first comma is the given-name segment, so a comma-separated middle name is retained (`"Smith, John, Robert"`keeps `Robert`) while trailing credentials are still stripped.

Requirements
------------

[](#requirements)

- PHP 8.3+ (tested through 8.5)
- `ext-mbstring`

Installation
------------

[](#installation)

```
composer require iliaal/nameparser
```

Usage
-----

[](#usage)

```
use Iliaal\NameParser\Parser;

$parser = new Parser();
$name = $parser->parse('Dr. Jane A. Doe DDS');

$name->getSalutation();   // "Dr."
$name->getFirstname();    // "Jane"
$name->getInitials();     // "A."
$name->getLastname();     // "Doe"
$name->getSuffix();       // "DDS"
$name->getFullName();     // "Jane A. Doe"
```

Beyond the example above, `Name` also exposes `getMiddlename()`, `getNickname()`, `getLastnamePrefix()`, `getGivenName()`, `getAll()`, `toArray()`, `getConfidence()`, and `getSource()`. `getLastname(true)` returns the surname without any particle prefix; the default `getLastname()` already includes prefixes.

### Structured output

[](#structured-output)

`toArray()` returns every part under a fixed key set, with an empty string for any part that is absent. Unlike `getAll()`, which omits empty parts and varies its keys, this shape is safe to consume without existence checks:

```
$parser->parse('Dr. Jane A. Doe DDS')->toArray();
// [
//   'salutation' => 'Dr.', 'firstname' => 'Jane', 'initials' => 'A.',
//   'middlename' => '', 'lastname_prefix' => '', 'lastname' => 'Doe',
//   'suffix' => 'DDS', 'nickname' => '', 'given_name' => 'Jane A.',
//   'full_name' => 'Jane A. Doe',
// ]
```

Note that `lastname` already includes any particle prefix (`de la Torre`); `lastname_prefix` is a convenience extract, not a component to prepend.

### Confidence / ambiguity

[](#confidence--ambiguity)

For batch imports where a wrong split is a data-integrity problem, check whether the input was decidable from its casing. The signal is available two ways: as a standalone pre-check on a raw string, or on the parsed result itself.

```
use Iliaal\NameParser\Confidence;

// pre-check, before parsing (default English ambiguous-key set)
$result = Confidence::assess('NGUYEN, VI');
// ['ambiguous' => true, 'notes' => ["'VI' could be a name or a credential; input casing is uniform"]]

// or read it off the parse; uses the same input and the parser's suffix dictionaries
$result = $parser->parse('NGUYEN, VI')->getConfidence();

if ($result['ambiguous']) {
    // queue the row for manual review instead of trusting the parse
}
```

`getConfidence()` is read-only and does not change what `parse()` returns; it is an advisory pass you opt into. A mixed-case input like `"Nguyen, Vi"` stays unflagged; the title-case `Vi` resolves to the given name.

For a non-default language set, standalone `Confidence::assess($string)` still uses the full English ambiguous-key table. To match a custom parser, either call `Name::getConfidence()` after `parse()`, or pass the parser's suffix dictionary as the second argument to `assess()`.

> **All-caps limitation.** Disambiguation keys off casing, so uniform-case input (all-caps legacy and registry data, or all-lowercase) carries no signal: an ambiguous trailing token reads as a credential by default. The confidence pass flags these when the token plausibly collides with a real name (`Do`, `Vi`, `Ma`, roman numerals, `MBA`), so you can route them to review. Clean credentials that are not also names (`RN`, `PT`, `OD`) are left unflagged to keep review volume manageable on all-caps datasets.

### Languages

[](#languages)

`new Parser()` uses the English dictionary. Passing languages **replaces** that list entirely (salutations, suffixes, and surname particles), it does not merge onto English. `new Parser([new German()])` gives German honorifics and ordinals only, not English professional credentials or English particles such as `van`.

Compose dictionaries when you need both:

```
use Iliaal\NameParser\Language\English;
use Iliaal\NameParser\Language\German;

$parser = new Parser([new English(), new German()]);
```

Dictionary keys merge in constructor order, and the first language wins on collisions. With English first, `Fr.` resolves to `Fr.`. With German first, it resolves to `Frau`.

### Configuration

[](#configuration)

Fluent setters on `Parser`:

- `setSurnameFirst(true)` reads comma-less space-separated names in CJK order (`Mao Zedong` → last `Mao`). Opt-in; romanized order cannot be auto-detected.
- `setNicknameDelimiters(['>'])` **replaces** the default pairs (`()[]{}` and quotes). An empty array restores the defaults; it does not disable nicknames.
- `setWhitespace`, `setMaxCombinedInitials`, `setMaxSalutationIndex` tune collapse and mapper gates.
- `setMappers([...])` replaces the single-segment (Western, no-comma) pipeline only. Comma forms and `setSurnameFirst(true)` use dedicated sub-parsers that always build their own mapper lists from the language dictionaries. Pass `[]`to restore the default pipeline.

### Parsing limits

[](#parsing-limits)

Some inputs have no structural signal. A comma followed only by credentials can mean full name plus credentials (`Jane Doe, MD`) or surname plus credentials (`Hidalgo Castillo, MD`). The parser keeps the left side in Western order in that case. Use an explicit given-name segment, for example `Hidalgo Castillo, Maria, MD`, or post-process feeds where the left side is a surname-only field.

Two-token surnames without particles are also ambiguous in space-separated names. `Jennifer Chen Wu` and `Mary Jo Li` share the same token structure, but one wants `Chen Wu` as a surname while the other wants `Jo` as a middle name. The parser keeps the existing compound-surname heuristic for two-character terminal surnames.

Unknown trailing credentials follow the same casing rule as the ambiguous tokens. When a known credential anchors the tail and the input is mixed-case, an adjacent unknown all-caps token is kept as a credential too: `John Smith MD FACS`and `Smith, John, MD, FACS` keep both in the suffix. A pure all-caps segment with no prior dictionary anchor is kept as a name (`Smith, JOHN, MD` → first `John`, suffix `MD`), because it is indistinguishable from an all-caps given name. Prefer the known credential first when the unknown stands alone (`Smith, MD, FACS`). Uniform all-caps rows cannot recover unknown credentials; with no case signal an unknown token could equally be a surname, so it stays in the name.

`getFullName()` and `toArray()['full_name']` are the given name plus surname only (no salutation, nickname, or suffix). `__toString()` is the richer display line from `getAll(true)` (salutation through suffix, nickname wrapped). Both drop comma structure and are not guaranteed to re-parse to the same fields, so treat them as output, not as a round-trippable serialization.

### Performance

[](#performance)

Reuse one `Parser` across a batch rather than constructing a new one per row. The parser memoizes its merged dictionaries, its mapper pipeline, and the comma-segment sub-parsers on first use, so a shared instance amortizes that setup across every `parse()` call.

Development
-----------

[](#development)

```
composer install
composer test     # phpunit
composer analyse  # phpstan (level 9)
composer lint     # php-cs-fixer (dry run)
```

Credits
-------

[](#credits)

Original library by [The Iconic](https://github.com/theiconic). Modernization to PHP 8.3+ by [Zachary Miller](https://github.com/CodeByZach). Casing/credential parsing and confidence signal in this fork by Ilia Alshanetsky.

License
-------

[](#license)

MIT. See [LICENSE](LICENSE). Upstream copyright notices are retained.

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance98

Actively maintained with recent releases

Popularity24

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~11 days

Total

4

Last Release

14d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2838354?v=4)[iliaa](/maintainers/iliaa)[@iliaa](https://github.com/iliaa)

---

Top Contributors

[![iliaal](https://avatars.githubusercontent.com/u/158724?v=4)](https://github.com/iliaal "iliaal (43 commits)")

---

Tags

credentialsfullnamehuman-namesname-parsername-parsingparserphptext-processingparsernamecredentialsfullnamename-parserhumanname

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/iliaal-nameparser/health.svg)

```
[![Health](https://phpackages.com/badges/iliaal-nameparser/health.svg)](https://phpackages.com/packages/iliaal-nameparser)
```

###  Alternatives

[nikic/php-parser

A PHP parser written in PHP

17.5k954.1M2.5k](/packages/nikic-php-parser)[doctrine/lexer

PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.

11.2k963.0M160](/packages/doctrine-lexer)[erusev/parsedown

Parser for Markdown.

15.1k156.8M875](/packages/erusev-parsedown)[league/commonmark

Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)

3.0k437.5M1.1k](/packages/league-commonmark)[masterminds/html5

An HTML5 parser and serializer.

1.8k269.7M327](/packages/masterminds-html5)[sabberworm/php-css-parser

Parser for CSS Files written in PHP

1.8k211.0M78](/packages/sabberworm-php-css-parser)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
