Browse Source

Fix inflection of camel case words (#14667)

By generating a pattern for both under_score and CamelCase irregular
words we can handle the new scenarios while also optimizing away a few
string allocations per inflector call.

Co-authored-by: Mark Story <mark@mark-story.com>
Mark Sch 5 years ago
parent
commit
0b52d7a17a
2 changed files with 36 additions and 8 deletions
  1. 24 8
      src/Utility/Inflector.php
  2. 12 0
      tests/TestCase/Utility/InflectorTest.php

+ 24 - 8
src/Utility/Inflector.php

@@ -271,10 +271,17 @@ class Inflector
         }
 
         if (!isset(static::$_cache['irregular']['pluralize'])) {
-            static::$_cache['irregular']['pluralize'] = '(?:' . implode('|', array_keys(static::$_irregular)) . ')';
+            $words = array_keys(static::$_irregular);
+            static::$_cache['irregular']['pluralize'] = '/(.*?(?:\\b|_))(' . implode('|', $words) . ')$/i';
+
+            $upperWords = array_map('ucfirst', $words);
+            static::$_cache['irregular']['upperPluralize'] = '/(.*?(?:\\b|[a-z]))(' . implode('|', $upperWords) . ')$/';
         }
 
-        if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['pluralize'] . ')$/i', $word, $regs)) {
+        if (
+            preg_match(static::$_cache['irregular']['pluralize'], $word, $regs) ||
+            preg_match(static::$_cache['irregular']['upperPluralize'], $word, $regs)
+        ) {
             static::$_cache['pluralize'][$word] = $regs[1] . substr($regs[2], 0, 1) .
                 substr(static::$_irregular[strtolower($regs[2])], 1);
 
@@ -282,10 +289,10 @@ class Inflector
         }
 
         if (!isset(static::$_cache['uninflected'])) {
-            static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')';
+            static::$_cache['uninflected'] = '/^(' . implode('|', static::$_uninflected) . ')$/i';
         }
 
-        if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) {
+        if (preg_match(static::$_cache['uninflected'], $word, $regs)) {
             static::$_cache['pluralize'][$word] = $word;
 
             return $word;
@@ -316,10 +323,19 @@ class Inflector
         }
 
         if (!isset(static::$_cache['irregular']['singular'])) {
-            static::$_cache['irregular']['singular'] = '(?:' . implode('|', static::$_irregular) . ')';
+            $wordList = array_values(static::$_irregular);
+            static::$_cache['irregular']['singular'] = '/(.*?(?:\\b|_))(' . implode('|', $wordList) . ')$/i';
+
+            $upperWordList = array_map('ucfirst', $wordList);
+            static::$_cache['irregular']['singularUpper'] = '/(.*?(?:\\b|[a-z]))(' .
+                implode('|', $upperWordList) .
+                ')$/';
         }
 
-        if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['singular'] . ')$/i', $word, $regs)) {
+        if (
+            preg_match(static::$_cache['irregular']['singular'], $word, $regs) ||
+            preg_match(static::$_cache['irregular']['singularUpper'], $word, $regs)
+        ) {
             $suffix = array_search(strtolower($regs[2]), static::$_irregular, true);
             $suffix = $suffix ? substr($suffix, 1) : '';
             static::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) . $suffix;
@@ -328,10 +344,10 @@ class Inflector
         }
 
         if (!isset(static::$_cache['uninflected'])) {
-            static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')';
+            static::$_cache['uninflected'] = '/^(' . implode('|', static::$_uninflected) . ')$/i';
         }
 
-        if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) {
+        if (preg_match(static::$_cache['uninflected'], $word, $regs)) {
             static::$_cache['pluralize'][$word] = $word;
 
             return $word;

+ 12 - 0
tests/TestCase/Utility/InflectorTest.php

@@ -498,6 +498,12 @@ class InflectorTest extends TestCase
         $this->assertSame('alertables', Inflector::pluralize('alert'));
         $this->assertSame('amazable', Inflector::pluralize('amaze'));
         $this->assertSame('phonezes', Inflector::pluralize('phone'));
+
+        $this->assertSame('criteria', Inflector::pluralize('criterion'));
+        $this->assertSame('test_criteria', Inflector::pluralize('test_criterion'));
+        $this->assertSame('Criteria', Inflector::pluralize('Criterion'));
+        $this->assertSame('TestCriteria', Inflector::pluralize('TestCriterion'));
+        $this->assertSame('Test Criteria', Inflector::pluralize('Test Criterion'));
     }
 
     /**
@@ -520,6 +526,12 @@ class InflectorTest extends TestCase
         $this->assertSame('inflecta', Inflector::singularize('inflectors'));
         $this->assertSame('contributa', Inflector::singularize('contributors'));
         $this->assertSame('singulars', Inflector::singularize('singulars'));
+
+        $this->assertSame('criterion', Inflector::singularize('criteria'));
+        $this->assertSame('test_criterion', Inflector::singularize('test_criteria'));
+        $this->assertSame('Criterion', Inflector::singularize('Criteria'));
+        $this->assertSame('TestCriterion', Inflector::singularize('TestCriteria'));
+        $this->assertSame('Test Criterion', Inflector::singularize('Test Criteria'));
     }
 
     /**