/** * @dataProvider singularizeProvider */ public function testSingularize($plural, $singular) { $single = Inflector::singularize($plural); if (is_string($singular) && is_array($single)) { $this->fail("--- Expected\n`string`: " . $singular . "\n+++ Actual\n`array`: " . implode(', ', $single)); } elseif (is_array($singular) && is_string($single)) { $this->fail("--- Expected\n`array`: " . implode(', ', $singular) . "\n+++ Actual\n`string`: " . $single); } $this->assertEquals($singular, $single); }
/** * Extracts a property name from a method name. * * @param string $methodName * @param \ReflectionProperty[] $reflectionProperties * * @return string */ private function getPropertyName($methodName, array $reflectionProperties) { $pattern = implode('|', array_merge(self::$accessorPrefixes, self::$mutatorPrefixes)); if (preg_match('/^(' . $pattern . ')(.+)$/i', $methodName, $matches)) { if (!in_array($matches[1], self::$arrayMutatorPrefixes)) { return $matches[2]; } foreach ($reflectionProperties as $reflectionProperty) { foreach ((array) Inflector::singularize($reflectionProperty->name) as $name) { if (strtolower($name) === strtolower($matches[2])) { return $reflectionProperty->name; } } } return $matches[2]; } }
/** * Returns the singular form of a word. * * If the method can't determine the form with certainty, an array of the * possible singulars is returned. * * @param string $plural A word in plural form * * @return string|array The singular form or an array of possible singular * forms * * @deprecated Deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector::singularize} instead. */ public static function singularify($plural) { @trigger_error('StringUtil::singularify() is deprecated since version 3.1 and will be removed in 4.0. Use Symfony\\Component\\Inflector\\Inflector::singularize instead.', E_USER_DEPRECATED); return Inflector::singularize($plural); }
/** * Guesses how to write the property value. * * @param string $class * @param string $property * @param mixed $value * * @return array */ private function getWriteAccessInfo($class, $property, $value) { $key = $class . '..' . $property; if (isset($this->writePropertyCache[$key])) { return $this->writePropertyCache[$key]; } if ($this->cacheItemPool) { $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_WRITE . str_replace('\\', '.', $key)); if ($item->isHit()) { return $this->writePropertyCache[$key] = $item->get(); } } $access = array(); $reflClass = new \ReflectionClass($class); $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property); $camelized = $this->camelize($property); $singulars = (array) Inflector::singularize($camelized); if (is_array($value) || $value instanceof \Traversable) { $methods = $this->findAdderAndRemover($reflClass, $singulars); if (null !== $methods) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; $access[self::ACCESS_ADDER] = $methods[0]; $access[self::ACCESS_REMOVER] = $methods[1]; } } if (!isset($access[self::ACCESS_TYPE])) { $setter = 'set' . $camelized; $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) if ($this->isMethodAccessible($reflClass, $setter, 1)) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; $access[self::ACCESS_NAME] = $setter; } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; $access[self::ACCESS_NAME] = $getsetter; } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_NAME] = $property; } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_NAME] = $property; } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) { // we call the getter and hope the __call do the job $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; $access[self::ACCESS_NAME] = $setter; } elseif (null !== ($methods = $this->findAdderAndRemover($reflClass, $singulars))) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; $access[self::ACCESS_NAME] = sprintf('The property "%s" in class "%s" can be defined with the methods "%s()" but ' . 'the new value must be an array or an instance of \\Traversable, ' . '"%s" given.', $property, $reflClass->name, implode('()", "', $methods), is_object($value) ? get_class($value) : gettype($value)); } else { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; $access[self::ACCESS_NAME] = sprintf('Neither the property "%s" nor one of the methods %s"%s()", "%s()", ' . '"__set()" or "__call()" exist and have public access in class "%s".', $property, implode('', array_map(function ($singular) { return '"add' . $singular . '()"/"remove' . $singular . '()", '; }, $singulars)), $setter, $getsetter, $reflClass->name); } } if (isset($item)) { $this->cacheItemPool->save($item->set($access)); } return $this->writePropertyCache[$key] = $access; }
/** * Guesses how to write the property value. * * @param string $class * @param string $property * @param mixed $value * * @return array */ private function getWriteAccessInfo($class, $property, $value) { $key = $class . '::' . $property; if (isset($this->writePropertyCache[$key])) { $access = $this->writePropertyCache[$key]; } else { $access = array(); $reflClass = new \ReflectionClass($class); $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property); $camelized = $this->camelize($property); $singulars = (array) Inflector::singularize($camelized); if (is_array($value) || $value instanceof \Traversable) { $methods = $this->findAdderAndRemover($reflClass, $singulars); if (null !== $methods) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; $access[self::ACCESS_ADDER] = $methods[0]; $access[self::ACCESS_REMOVER] = $methods[1]; } } if (!isset($access[self::ACCESS_TYPE])) { $setter = 'set' . $camelized; $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) if ($this->isMethodAccessible($reflClass, $setter, 1)) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; $access[self::ACCESS_NAME] = $setter; } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; $access[self::ACCESS_NAME] = $getsetter; } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_NAME] = $property; } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_NAME] = $property; } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) { // we call the getter and hope the __call do the job $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; $access[self::ACCESS_NAME] = $setter; } else { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; $access[self::ACCESS_NAME] = sprintf('Neither the property "%s" nor one of the methods %s"%s()", "%s()", ' . '"__set()" or "__call()" exist and have public access in class "%s".', $property, implode('', array_map(function ($singular) { return '"add' . $singular . '()"/"remove' . $singular . '()", '; }, $singulars)), $setter, $getsetter, $reflClass->name); } } $this->writePropertyCache[$key] = $access; } return $access; }