Skip to content

Fix singularization of words ending in -ives - #351

Open
dylanpulver wants to merge 1 commit into
doctrine:2.1.xfrom
dylanpulver:fix/singularize-ives
Open

Fix singularization of words ending in -ives#351
dylanpulver wants to merge 1 commit into
doctrine:2.1.xfrom
dylanpulver:fix/singularize-ives

Conversation

@dylanpulver

@dylanpulver dylanpulver commented Sep 2, 2026

Copy link
Copy Markdown

([^fo])ves$ -> \1fe turns every -ives plural into -ife, so on 2.1.0:

$inflector->singularize('explosives'); // explosife
$inflector->singularize('adhesives');  // adhesife
$inflector->singularize('reflexives'); // reflexife

Only knife, wife and life lose the v; every other -ive noun keeps it. archives and olives come out right today only because the (hive)s$ and (olive)s$ entries in front of that rule catch them, a whitelist that grew one word at a time ((dive)s$ for issue #44, (olive)s$ in #67).

This replaces the five-entry whitelist with three rules: explicit knives/wives/lives, then a general (ive)s$. Net two rules fewer.

Measurement. Oracle: no English singular ending in -ve loses the v when pluralized, so singularize($w.'s') === $w must hold. Corpus: the 2,487 words matching ^[a-z]{3,}ve$ in /usr/share/dict/words.

misses / 2487
2.1.x 796
adding (sive)s$ to the whitelist 443
this PR 332

The middle row is worth stating: the smaller fix, one more whitelist entry in the style of the existing ones, passes the full suite, phpcs and phpstan, and passes two of the three cases added here. It is only distinguishable by corpus size and by reflexives, which is why that case is in the test data.

Blast radius, bounded rather than asserted: across 471,282 strings, output changes for exactly 482; every one ends in ives, every one changes -ife to -ive, and no word whose output was already correct changes.

The remaining 332 misses are the separate ([lr])ves$ family (nerves -> nerf). Untouched on purpose, different rule.

Checks. phpunit 1213 -> 1223 green both sides; phpcs clean; phpstan no errors; composer validate --strict valid. No expectation changed. Does not overlap #350, which touches getIrregular().

Disclosure: prepared with AI assistance (Claude Opus 5, claude-opus-5). I have reviewed the diff and verified every measurement above myself.

The generic rule ([^fo])ves$ -> \1fe turned every -ives plural into
-ife, so explosives singularized to explosife and adhesives to adhesife.
A whitelist of tive/hive/drive/dive/olive had grown in front of it one
word at a time, which is why archives and olives happened to work while
the rest of the family did not.

Replaced that whitelist with explicit knives/wives/lives rules plus a
general (ive)s$ rule, which is two rules shorter and covers the whole
family.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant