[PropertyInfo] Import the component
This commit is contained in:
parent
d1ae400cb1
commit
f1eb185236
@ -48,6 +48,7 @@
|
||||
"symfony/options-resolver": "self.version",
|
||||
"symfony/process": "self.version",
|
||||
"symfony/property-access": "self.version",
|
||||
"symfony/property-info": "self.version",
|
||||
"symfony/proxy-manager-bridge": "self.version",
|
||||
"symfony/routing": "self.version",
|
||||
"symfony/security": "self.version",
|
||||
@ -76,7 +77,11 @@
|
||||
"monolog/monolog": "~1.11",
|
||||
"ircmaxell/password-compat": "~1.0",
|
||||
"ocramius/proxy-manager": "~0.4|~1.0",
|
||||
"egulias/email-validator": "~1.2"
|
||||
"egulias/email-validator": "~1.2",
|
||||
"phpdocumentor/reflection": "^1.0.7"
|
||||
},
|
||||
"conflict": {
|
||||
"phpdocumentor/reflection": "<1.0.7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
149
src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
Normal file
149
src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\PropertyInfo;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\Common\Persistence\Mapping\MappingException;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* Extracts data using Doctrine ORM and ODM metadata.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadataFactory
|
||||
*/
|
||||
private $classMetadataFactory;
|
||||
|
||||
public function __construct(ClassMetadataFactory $classMetadataFactory)
|
||||
{
|
||||
$this->classMetadataFactory = $classMetadataFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties($class, array $context = array())
|
||||
{
|
||||
try {
|
||||
$metadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
} catch (MappingException $exception) {
|
||||
return;
|
||||
}
|
||||
|
||||
return array_merge($metadata->getFieldNames(), $metadata->getAssociationNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes($class, $property, array $context = array())
|
||||
{
|
||||
try {
|
||||
$metadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
} catch (MappingException $exception) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($metadata->hasAssociation($property)) {
|
||||
$class = $metadata->getAssociationTargetClass($property);
|
||||
|
||||
if ($metadata->isSingleValuedAssociation($property)) {
|
||||
if ($metadata instanceof ClassMetadataInfo) {
|
||||
$nullable = isset($metadata->discriminatorColumn['nullable']) ? $metadata->discriminatorColumn['nullable'] : false;
|
||||
} else {
|
||||
$nullable = false;
|
||||
}
|
||||
|
||||
return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class));
|
||||
}
|
||||
|
||||
return array(new Type(
|
||||
Type::BUILTIN_TYPE_OBJECT,
|
||||
false,
|
||||
'Doctrine\Common\Collections\Collection',
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_INT),
|
||||
new Type(Type::BUILTIN_TYPE_OBJECT, false, $class)
|
||||
));
|
||||
}
|
||||
|
||||
if ($metadata->hasField($property)) {
|
||||
$typeOfField = $metadata->getTypeOfField($property);
|
||||
$nullable = $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property);
|
||||
|
||||
switch ($typeOfField) {
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
case 'datetimetz':
|
||||
case 'time':
|
||||
return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime'));
|
||||
|
||||
case 'array':
|
||||
return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true));
|
||||
|
||||
case 'simple_array':
|
||||
return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)));
|
||||
|
||||
case 'json_array':
|
||||
return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true));
|
||||
|
||||
default:
|
||||
return array(new Type($this->getPhpType($typeOfField), $nullable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the corresponding built-in PHP type.
|
||||
*
|
||||
* @param string $doctrineType
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getPhpType($doctrineType)
|
||||
{
|
||||
switch ($doctrineType) {
|
||||
case 'smallint':
|
||||
// No break
|
||||
case 'bigint':
|
||||
// No break
|
||||
case 'integer':
|
||||
return Type::BUILTIN_TYPE_INT;
|
||||
|
||||
case 'decimal':
|
||||
return Type::BUILTIN_TYPE_FLOAT;
|
||||
|
||||
case 'text':
|
||||
// No break
|
||||
case 'guid':
|
||||
return Type::BUILTIN_TYPE_STRING;
|
||||
|
||||
case 'boolean':
|
||||
return Type::BUILTIN_TYPE_BOOL;
|
||||
|
||||
case 'blob':
|
||||
// No break
|
||||
case 'binary':
|
||||
return Type::BUILTIN_TYPE_RESOURCE;
|
||||
|
||||
default:
|
||||
return $doctrineType;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\PropertyInfo\Tests;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Tools\Setup;
|
||||
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DoctrineExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var DoctrineExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'), true);
|
||||
$entityManager = EntityManager::create(array('driver' => 'pdo_sqlite'), $config);
|
||||
|
||||
$this->extractor = new DoctrineExtractor($entityManager->getMetadataFactory());
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'id',
|
||||
'guid',
|
||||
'time',
|
||||
'json',
|
||||
'simpleArray',
|
||||
'bool',
|
||||
'binary',
|
||||
'foo',
|
||||
'bar',
|
||||
),
|
||||
$this->extractor->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesProvider
|
||||
*/
|
||||
public function testExtract($property, array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy', $property, array()));
|
||||
}
|
||||
|
||||
public function typesProvider()
|
||||
{
|
||||
return array(
|
||||
array('id', array(new Type(Type::BUILTIN_TYPE_INT))),
|
||||
array('guid', array(new Type(Type::BUILTIN_TYPE_STRING))),
|
||||
array('bool', array(new Type(Type::BUILTIN_TYPE_BOOL))),
|
||||
array('binary', array(new Type(Type::BUILTIN_TYPE_RESOURCE))),
|
||||
array('json', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true))),
|
||||
array('foo', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation'))),
|
||||
array('bar', array(new Type(
|
||||
Type::BUILTIN_TYPE_OBJECT,
|
||||
false,
|
||||
'Doctrine\Common\Collections\Collection',
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_INT),
|
||||
new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation')
|
||||
))),
|
||||
array('simpleArray', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))),
|
||||
array('notMapped', null),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\ManyToMany;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DoctrineDummy
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="smallint")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DoctrineRelation")
|
||||
*/
|
||||
public $foo;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="DoctrineRelation")
|
||||
*/
|
||||
public $bar;
|
||||
|
||||
/**
|
||||
* @Column(type="guid")
|
||||
*/
|
||||
protected $guid;
|
||||
|
||||
/**
|
||||
* @Column(type="time")
|
||||
*/
|
||||
private $time;
|
||||
|
||||
/**
|
||||
* @Column(type="json_array")
|
||||
*/
|
||||
private $json;
|
||||
|
||||
/**
|
||||
* @Column(type="simple_array")
|
||||
*/
|
||||
private $simpleArray;
|
||||
|
||||
/**
|
||||
* @Column(type="boolean")
|
||||
*/
|
||||
private $bool;
|
||||
|
||||
/**
|
||||
* @Column(type="binary")
|
||||
*/
|
||||
private $binary;
|
||||
|
||||
public $notMapped;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DoctrineRelation
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="smallint")
|
||||
*/
|
||||
public $id;
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
"symfony/form": "~2.8|~3.0.0",
|
||||
"symfony/http-kernel": "~2.2|~3.0.0",
|
||||
"symfony/property-access": "~2.3|~3.0.0",
|
||||
"symfony/property-info": "~2.8|3.0",
|
||||
"symfony/security": "~2.2|~3.0.0",
|
||||
"symfony/expression-language": "~2.2|~3.0.0",
|
||||
"symfony/validator": "~2.5,>=2.5.5|~3.0.0",
|
||||
@ -37,6 +38,7 @@
|
||||
"suggest": {
|
||||
"symfony/form": "",
|
||||
"symfony/validator": "",
|
||||
"symfony/property-info": "",
|
||||
"doctrine/data-fixtures": "",
|
||||
"doctrine/dbal": "",
|
||||
"doctrine/orm": ""
|
||||
|
392
src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
Normal file
392
src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
Normal file
@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use phpDocumentor\Reflection\ClassReflector;
|
||||
use phpDocumentor\Reflection\DocBlock;
|
||||
use phpDocumentor\Reflection\FileReflector;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* Extracts data using a PHPDoc parser.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PhpDocExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface
|
||||
{
|
||||
const PROPERTY = 0;
|
||||
const ACCESSOR = 1;
|
||||
const MUTATOR = 2;
|
||||
|
||||
/**
|
||||
* @var FileReflector[]
|
||||
*/
|
||||
private $fileReflectors = array();
|
||||
|
||||
/**
|
||||
* @var DocBlock[]
|
||||
*/
|
||||
private $docBlocks = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getShortDescription($class, $property, array $context = array())
|
||||
{
|
||||
list($docBlock) = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
$shortDescription = $docBlock->getShortDescription();
|
||||
if ($shortDescription) {
|
||||
return $shortDescription;
|
||||
}
|
||||
|
||||
foreach ($docBlock->getTagsByName('var') as $var) {
|
||||
$parsedDescription = $var->getParsedDescription();
|
||||
|
||||
if (isset($parsedDescription[0]) && '' !== $parsedDescription[0]) {
|
||||
return $parsedDescription[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLongDescription($class, $property, array $context = array())
|
||||
{
|
||||
list($docBlock) = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = $docBlock->getLongDescription()->getContents();
|
||||
|
||||
return '' === $contents ? null : $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes($class, $property, array $context = array())
|
||||
{
|
||||
list($docBlock, $source, $prefix) = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($source) {
|
||||
case self::PROPERTY:
|
||||
$tag = 'var';
|
||||
break;
|
||||
|
||||
case self::ACCESSOR:
|
||||
$tag = 'return';
|
||||
break;
|
||||
|
||||
case self::MUTATOR:
|
||||
$tag = 'param';
|
||||
break;
|
||||
}
|
||||
|
||||
$types = array();
|
||||
foreach ($docBlock->getTagsByName($tag) as $tag) {
|
||||
$varTypes = $tag->getTypes();
|
||||
|
||||
// If null is present, all types are nullable
|
||||
$nullKey = array_search(Type::BUILTIN_TYPE_NULL, $varTypes);
|
||||
$nullable = false !== $nullKey;
|
||||
|
||||
// Remove the null type from the type if other types are defined
|
||||
if ($nullable && count($varTypes) > 1) {
|
||||
unset($varTypes[$nullKey]);
|
||||
}
|
||||
|
||||
foreach ($varTypes as $varType) {
|
||||
$type = $this->createType($varType, $nullable);
|
||||
if (null !== $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($types[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($prefix, ReflectionExtractor::$arrayMutatorPrefixes)) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
return array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the FileReflector associated with the class.
|
||||
*
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*
|
||||
* @return FileReflector|null
|
||||
*/
|
||||
private function getFileReflector(\ReflectionClass $reflectionClass)
|
||||
{
|
||||
if (!($fileName = $reflectionClass->getFileName()) || 'hh' === pathinfo($fileName, PATHINFO_EXTENSION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->fileReflectors[$fileName])) {
|
||||
return $this->fileReflectors[$fileName];
|
||||
}
|
||||
|
||||
$this->fileReflectors[$fileName] = new FileReflector($fileName);
|
||||
$this->fileReflectors[$fileName]->process();
|
||||
|
||||
return $this->fileReflectors[$fileName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DocBlock for this property.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getDocBlock($class, $property)
|
||||
{
|
||||
$propertyHash = sprintf('%s::%s', $class, $property);
|
||||
|
||||
if (isset($this->docBlocks[$propertyHash])) {
|
||||
return $this->docBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
$ucFirstProperty = ucfirst($property);
|
||||
|
||||
switch (true) {
|
||||
case $docBlock = $this->getDocBlockFromProperty($class, $property):
|
||||
$data = array($docBlock, self::PROPERTY, null);
|
||||
break;
|
||||
|
||||
case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
|
||||
$data = array($docBlock, self::ACCESSOR, null);
|
||||
break;
|
||||
|
||||
case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
|
||||
$data = array($docBlock, self::MUTATOR, $prefix);
|
||||
break;
|
||||
|
||||
default:
|
||||
$data = array(null, null);
|
||||
}
|
||||
|
||||
return $this->docBlocks[$propertyHash] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DocBlock from a property.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return DocBlock|null
|
||||
*/
|
||||
private function getDocBlockFromProperty($class, $property)
|
||||
{
|
||||
// Use a ReflectionProperty instead of $class to get the parent class if applicable
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
return;
|
||||
}
|
||||
|
||||
$reflectionCLass = $reflectionProperty->getDeclaringClass();
|
||||
|
||||
$fileReflector = $this->getFileReflector($reflectionCLass);
|
||||
if (!$fileReflector) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($fileReflector->getClasses() as $classReflector) {
|
||||
$className = $this->getClassName($classReflector);
|
||||
|
||||
if ($className === $reflectionCLass->name) {
|
||||
foreach ($classReflector->getProperties() as $propertyReflector) {
|
||||
// strip the $ prefix
|
||||
$propertyName = substr($propertyReflector->getName(), 1);
|
||||
|
||||
if ($propertyName === $property) {
|
||||
return $propertyReflector->getDocBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets DocBlock from accessor or mutator method.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $ucFirstProperty
|
||||
* @param int $type
|
||||
*
|
||||
* @return DocBlock|null
|
||||
*/
|
||||
private function getDocBlockFromMethod($class, $ucFirstProperty, $type)
|
||||
{
|
||||
$prefixes = $type === self::ACCESSOR ? ReflectionExtractor::$accessorPrefixes : ReflectionExtractor::$mutatorPrefixes;
|
||||
|
||||
foreach ($prefixes as $prefix) {
|
||||
$methodName = $prefix.$ucFirstProperty;
|
||||
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $methodName);
|
||||
|
||||
if (
|
||||
(self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters()) ||
|
||||
(self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($reflectionMethod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$reflectionClass = $reflectionMethod->getDeclaringClass();
|
||||
$fileReflector = $this->getFileReflector($reflectionClass);
|
||||
|
||||
if (!$fileReflector) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($fileReflector->getClasses() as $classReflector) {
|
||||
$className = $this->getClassName($classReflector);
|
||||
|
||||
if ($className === $reflectionClass->name) {
|
||||
if ($methodReflector = $classReflector->getMethod($methodName)) {
|
||||
return array($methodReflector->getDocBlock(), $prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the normalized class name (without trailing backslash).
|
||||
*
|
||||
* @param ClassReflector $classReflector
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getClassName(ClassReflector $classReflector)
|
||||
{
|
||||
$className = $classReflector->getName();
|
||||
if ('\\' === $className[0]) {
|
||||
return substr($className, 1);
|
||||
}
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@see Type} from a PHPDoc type.
|
||||
*
|
||||
* @param string $docType
|
||||
* @param bool $nullable
|
||||
*
|
||||
* @return Type|null
|
||||
*/
|
||||
private function createType($docType, $nullable)
|
||||
{
|
||||
// Cannot guess
|
||||
if (!$docType || 'mixed' === $docType) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($collection = '[]' === substr($docType, -2)) {
|
||||
$docType = substr($docType, 0, -2);
|
||||
}
|
||||
|
||||
$docType = $this->normalizeType($docType);
|
||||
list($phpType, $class) = $this->getPhpTypeAndClass($docType);
|
||||
|
||||
$array = 'array' === $docType;
|
||||
|
||||
if ($collection || $array) {
|
||||
if ($array || 'mixed' === $docType) {
|
||||
$collectionKeyType = null;
|
||||
$collectionValueType = null;
|
||||
} else {
|
||||
$collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
|
||||
$collectionValueType = new Type($phpType, false, $class);
|
||||
}
|
||||
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, $collectionKeyType, $collectionValueType);
|
||||
}
|
||||
|
||||
return new Type($phpType, $nullable, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the type.
|
||||
*
|
||||
* @param string $docType
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function normalizeType($docType)
|
||||
{
|
||||
switch ($docType) {
|
||||
case 'integer':
|
||||
return 'int';
|
||||
|
||||
case 'boolean':
|
||||
return 'bool';
|
||||
|
||||
// real is not part of the PHPDoc standard, so we ignore it
|
||||
case 'double':
|
||||
return 'float';
|
||||
|
||||
case 'callback':
|
||||
return 'callable';
|
||||
|
||||
case 'void':
|
||||
return 'null';
|
||||
|
||||
default:
|
||||
return $docType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array containing the PHP type and the class.
|
||||
*
|
||||
* @param string $docType
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getPhpTypeAndClass($docType)
|
||||
{
|
||||
if (in_array($docType, Type::$builtinTypes)) {
|
||||
return array($docType, null);
|
||||
}
|
||||
|
||||
return array('object', substr($docType, 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* Extracts PHP informations using the reflection API.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $mutatorPrefixes = array('add', 'remove', 'set');
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $accessorPrefixes = array('is', 'can', 'get');
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
public static $arrayMutatorPrefixes = array('add', 'remove');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties($class, array $context = array())
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = array();
|
||||
foreach ($reflectionClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflectionProperty) {
|
||||
$properties[$reflectionProperty->name] = true;
|
||||
}
|
||||
|
||||
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
|
||||
$propertyName = $this->getPropertyName($reflectionMethod->name);
|
||||
if ($propertyName) {
|
||||
$properties[$propertyName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes($class, $property, array $context = array())
|
||||
{
|
||||
if ($fromMutator = $this->extractFromMutator($class, $property)) {
|
||||
return $fromMutator;
|
||||
}
|
||||
|
||||
if ($fromAccessor = $this->extractFromAccessor($class, $property)) {
|
||||
return $fromAccessor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($class, $property, array $context = array())
|
||||
{
|
||||
if ($this->isPublicProperty($class, $property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
list($reflectionMethod) = $this->getAccessorMethod($class, $property);
|
||||
|
||||
return null !== $reflectionMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($class, $property, array $context = array())
|
||||
{
|
||||
if ($this->isPublicProperty($class, $property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
list($reflectionMethod) = $this->getMutatorMethod($class, $property);
|
||||
|
||||
return null !== $reflectionMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to extract type information from mutators.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return Type[]|null
|
||||
*/
|
||||
private function extractFromMutator($class, $property)
|
||||
{
|
||||
list($reflectionMethod, $prefix) = $this->getMutatorMethod($class, $property);
|
||||
if (null === $reflectionMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
$reflectionParameters = $reflectionMethod->getParameters();
|
||||
$reflectionParameter = $reflectionParameters[0];
|
||||
|
||||
$arrayMutator = in_array($prefix, self::$arrayMutatorPrefixes);
|
||||
|
||||
if (method_exists($reflectionParameter, 'getType') && $reflectionType = $reflectionParameter->getType()) {
|
||||
$fromReflectionType = $this->extractFromReflectionType($reflectionType);
|
||||
|
||||
if (!$arrayMutator) {
|
||||
return array($fromReflectionType);
|
||||
}
|
||||
|
||||
$phpType = Type::BUILTIN_TYPE_ARRAY;
|
||||
$collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
|
||||
$collectionValueType = $fromReflectionType;
|
||||
}
|
||||
|
||||
if ($reflectionParameter->isArray()) {
|
||||
$phpType = Type::BUILTIN_TYPE_ARRAY;
|
||||
$collection = true;
|
||||
}
|
||||
|
||||
if ($arrayMutator) {
|
||||
$collection = true;
|
||||
$nullable = false;
|
||||
$collectionNullable = $reflectionParameter->allowsNull();
|
||||
} else {
|
||||
$nullable = $reflectionParameter->allowsNull();
|
||||
$collectionNullable = false;
|
||||
}
|
||||
|
||||
if (!isset($collection)) {
|
||||
$collection = false;
|
||||
}
|
||||
|
||||
if (method_exists($reflectionParameter, 'isCallable') && $reflectionParameter->isCallable()) {
|
||||
$phpType = Type::BUILTIN_TYPE_CALLABLE;
|
||||
}
|
||||
|
||||
if ($typeHint = $reflectionParameter->getClass()) {
|
||||
if ($collection) {
|
||||
$phpType = Type::BUILTIN_TYPE_ARRAY;
|
||||
$collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
|
||||
$collectionValueType = new Type(Type::BUILTIN_TYPE_OBJECT, $collectionNullable, $typeHint->name);
|
||||
} else {
|
||||
$phpType = Type::BUILTIN_TYPE_OBJECT;
|
||||
$typeClass = $typeHint->name;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing useful extracted
|
||||
if (!isset($phpType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return array(
|
||||
new Type(
|
||||
$phpType,
|
||||
$nullable,
|
||||
isset($typeClass) ? $typeClass : null,
|
||||
$collection,
|
||||
isset($collectionKeyType) ? $collectionKeyType : null,
|
||||
isset($collectionValueType) ? $collectionValueType : null
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to extract type information from accessors.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return Type[]|null
|
||||
*/
|
||||
private function extractFromAccessor($class, $property)
|
||||
{
|
||||
list($reflectionMethod, $prefix) = $this->getAccessorMethod($class, $property);
|
||||
if (null === $reflectionMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method_exists($reflectionMethod, 'getReturnType') && $reflectionType = $reflectionMethod->getReturnType()) {
|
||||
return array($this->extractFromReflectionType($reflectionType));
|
||||
}
|
||||
|
||||
if (in_array($prefix, array('is', 'can'))) {
|
||||
return array(new Type(Type::BUILTIN_TYPE_BOOL));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts data from the PHP 7 reflection type.
|
||||
*
|
||||
* @param \ReflectionType $reflectionType
|
||||
*
|
||||
* @return Type
|
||||
*/
|
||||
private function extractFromReflectionType(\ReflectionType $reflectionType)
|
||||
{
|
||||
$phpTypeOrClass = (string) $reflectionType;
|
||||
$nullable = $reflectionType->allowsNull();
|
||||
|
||||
if ($reflectionType->isBuiltin()) {
|
||||
if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
|
||||
$type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true);
|
||||
} else {
|
||||
$type = new Type($phpTypeOrClass, $nullable);
|
||||
}
|
||||
} else {
|
||||
$type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $phpTypeOrClass);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the class have the given public property?
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isPublicProperty($class, $property)
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
|
||||
return $reflectionProperty->isPublic();
|
||||
} catch (\ReflectionException $reflectionExcetion) {
|
||||
// Return false if the property doesn't exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the accessor method.
|
||||
*
|
||||
* Returns an array with a the instance of \ReflectionMethod as first key
|
||||
* and the prefix of the method as second or null if not found.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getAccessorMethod($class, $property)
|
||||
{
|
||||
$ucProperty = ucfirst($property);
|
||||
|
||||
foreach (self::$accessorPrefixes as $prefix) {
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $prefix.$ucProperty);
|
||||
|
||||
if (0 === $reflectionMethod->getNumberOfRequiredParameters()) {
|
||||
return array($reflectionMethod, $prefix);
|
||||
}
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
// Return null if the property doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mutator method.
|
||||
*
|
||||
* Returns an array with a the instance of \ReflectionMethod as first key
|
||||
* and the prefix of the method as second or null if not found.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getMutatorMethod($class, $property)
|
||||
{
|
||||
$ucProperty = ucfirst($property);
|
||||
|
||||
foreach (self::$mutatorPrefixes as $prefix) {
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $prefix.$ucProperty);
|
||||
|
||||
// Parameter can be optional to allow things like: method(array $foo = null)
|
||||
if ($reflectionMethod->getNumberOfParameters() >= 1) {
|
||||
return array($reflectionMethod, $prefix);
|
||||
}
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a property name from a method name.
|
||||
*
|
||||
* @param string $methodName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getPropertyName($methodName)
|
||||
{
|
||||
$pattern = implode('|', array_merge(self::$accessorPrefixes, self::$mutatorPrefixes));
|
||||
|
||||
if (preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
|
||||
return $matches[2];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* Lists available properties using Symfony Serializer Component metadata.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class SerializerExtractor implements PropertyListExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadataFactoryInterface
|
||||
*/
|
||||
private $classMetadataFactory;
|
||||
|
||||
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
|
||||
{
|
||||
$this->classMetadataFactory = $classMetadataFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties($class, array $context = array())
|
||||
{
|
||||
if (!isset($context['serializer_groups']) || !is_array($context['serializer_groups'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->classMetadataFactory->getMetadataFor($class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = array();
|
||||
$serializerClassMetadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
|
||||
foreach ($serializerClassMetadata->getAttributesMetadata() as $serializerAttributeMetadata) {
|
||||
if (count(array_intersect($context['serializer_groups'], $serializerAttributeMetadata->getGroups())) > 0) {
|
||||
$properties[] = $serializerAttributeMetadata->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
19
src/Symfony/Component/PropertyInfo/LICENSE
Normal file
19
src/Symfony/Component/PropertyInfo/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Guesses if the property can be accessed or mutated.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyAccessExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Is the property readable?
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
* @param array $context
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isReadable($class, $property, array $context = array());
|
||||
|
||||
/**
|
||||
* Is the property writable?
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
* @param array $context
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isWritable($class, $property, array $context = array());
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Description extractor Interface.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyDescriptionExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the short description of the property.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
* @param array $context
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getShortDescription($class, $property, array $context = array());
|
||||
|
||||
/**
|
||||
* Gets the long description of the property.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
* @param array $context
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLongDescription($class, $property, array $context = array());
|
||||
}
|
121
src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php
Normal file
121
src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Default {@see PropertyInfoExtractorInterface} implementation.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyInfoExtractor implements PropertyInfoExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @var PropertyListExtractorInterface[]
|
||||
*/
|
||||
private $listExtractors;
|
||||
|
||||
/**
|
||||
* @var PropertyTypeExtractorInterface[]
|
||||
*/
|
||||
private $typeExtractors;
|
||||
|
||||
/**
|
||||
* @var PropertyDescriptionExtractorInterface[]
|
||||
*/
|
||||
private $descriptionExtractors;
|
||||
|
||||
/**
|
||||
* @var PropertyAccessExtractorInterface[]
|
||||
*/
|
||||
private $accessExtractors;
|
||||
|
||||
/**
|
||||
* @param PropertyListExtractorInterface[] $listExtractors
|
||||
* @param PropertyTypeExtractorInterface[] $typeExtractors
|
||||
* @param PropertyDescriptionExtractorInterface[] $descriptionExtractors
|
||||
* @param PropertyAccessExtractorInterface[] $accessExtractors
|
||||
*/
|
||||
public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array())
|
||||
{
|
||||
$this->listExtractors = $listExtractors;
|
||||
$this->typeExtractors = $typeExtractors;
|
||||
$this->descriptionExtractors = $descriptionExtractors;
|
||||
$this->accessExtractors = $accessExtractors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties($class, array $context = array())
|
||||
{
|
||||
return $this->extract($this->listExtractors, 'getProperties', array($class, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getShortDescription($class, $property, array $context = array())
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getShortDescription', array($class, $property, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLongDescription($class, $property, array $context = array())
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getLongDescription', array($class, $property, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes($class, $property, array $context = array())
|
||||
{
|
||||
return $this->extract($this->typeExtractors, 'getTypes', array($class, $property, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($class, $property, array $context = array())
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isReadable', array($class, $property, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($class, $property, array $context = array())
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isWritable', array($class, $property, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over registered extractors and return the first value found.
|
||||
*
|
||||
* @param array $extractors
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function extract(array $extractors, $method, array $arguments)
|
||||
{
|
||||
foreach ($extractors as $extractor) {
|
||||
$value = call_user_func_array(array($extractor, $method), $arguments);
|
||||
if (null !== $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Gets info about PHP class properties.
|
||||
*
|
||||
* A convenient interface inheriting all specific info interfaces.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyInfoExtractorInterface extends PropertyTypeExtractorInterface, PropertyDescriptionExtractorInterface, PropertyAccessExtractorInterface, PropertyListExtractorInterface
|
||||
{
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Extracts the list of properties available for the given class.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyListExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the list of properties available for the given class.
|
||||
*
|
||||
* @param string $class
|
||||
* @param array $context
|
||||
*
|
||||
* @return string[]|null
|
||||
*/
|
||||
public function getProperties($class, array $context = array());
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Type Extractor Interface.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets types of a property.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
* @param array $context
|
||||
*
|
||||
* @return Type[]|null
|
||||
*/
|
||||
public function getTypes($class, $property, array $context = array());
|
||||
}
|
14
src/Symfony/Component/PropertyInfo/README.md
Normal file
14
src/Symfony/Component/PropertyInfo/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
PropertyInfo Component
|
||||
======================
|
||||
|
||||
PropertyInfo extracts information about PHP class' properties using metadata
|
||||
of popular sources.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
You can run the unit tests with the following command:
|
||||
|
||||
$ cd path/to/Symfony/Component/PropertyInfo/
|
||||
$ composer install
|
||||
$ phpunit
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractors;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PhpDocExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var PhpDocExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->extractor = new PhpDocExtractor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesProvider
|
||||
*/
|
||||
public function testExtract($property, array $type = null, $shortDescription, $longDescription)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
$this->assertSame($shortDescription, $this->extractor->getShortDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public function typesProvider()
|
||||
{
|
||||
return array(
|
||||
array('foo', null, 'Short description.', 'Long description.'),
|
||||
array('bar', array(new Type(Type::BUILTIN_TYPE_STRING)), 'This is bar.', null),
|
||||
array('baz', array(new Type(Type::BUILTIN_TYPE_INT)), 'Should be used.', null),
|
||||
array('foo2', array(new Type(Type::BUILTIN_TYPE_FLOAT)), null, null),
|
||||
array('foo3', array(new Type(Type::BUILTIN_TYPE_CALLABLE)), null, null),
|
||||
array('foo4', array(new Type(Type::BUILTIN_TYPE_NULL)), null, null),
|
||||
array('foo5', null, null, null),
|
||||
array(
|
||||
'files',
|
||||
array(
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
),
|
||||
null,
|
||||
null,
|
||||
),
|
||||
array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null),
|
||||
array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null),
|
||||
array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
|
||||
array('a', array(new Type(Type::BUILTIN_TYPE_INT)), 'A.', null),
|
||||
array('b', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), 'B.', null),
|
||||
array('c', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null),
|
||||
array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null),
|
||||
array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null),
|
||||
array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ReflectionExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var ReflectionExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->extractor = new ReflectionExtractor();
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'bal',
|
||||
'parent',
|
||||
'collection',
|
||||
'foo',
|
||||
'foo2',
|
||||
'foo3',
|
||||
'foo4',
|
||||
'foo5',
|
||||
'files',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
),
|
||||
$this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesProvider
|
||||
*/
|
||||
public function testExtractors($property, array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, array()));
|
||||
}
|
||||
|
||||
public function typesProvider()
|
||||
{
|
||||
return array(
|
||||
array('a', null),
|
||||
array('b', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy'))),
|
||||
array('c', array(new Type(Type::BUILTIN_TYPE_BOOL))),
|
||||
array('d', array(new Type(Type::BUILTIN_TYPE_BOOL))),
|
||||
array('e', null),
|
||||
array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php7TypesProvider
|
||||
*/
|
||||
public function testExtractPhp7Type($property, array $type = null)
|
||||
{
|
||||
if (!method_exists('\ReflectionMethod', 'getReturnType')) {
|
||||
$this->markTestSkipped('Available only with PHP 7 and superior.');
|
||||
}
|
||||
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy', $property, array()));
|
||||
}
|
||||
|
||||
public function php7TypesProvider()
|
||||
{
|
||||
return array(
|
||||
array('foo', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true))),
|
||||
array('bar', array(new Type(Type::BUILTIN_TYPE_INT))),
|
||||
array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))),
|
||||
);
|
||||
}
|
||||
|
||||
public function testIsReadable()
|
||||
{
|
||||
$this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'bar', array()));
|
||||
$this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'baz', array()));
|
||||
$this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'parent', array()));
|
||||
$this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'a', array()));
|
||||
$this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'b', array()));
|
||||
$this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'c', array()));
|
||||
$this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'd', array()));
|
||||
$this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'e', array()));
|
||||
$this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'f', array()));
|
||||
}
|
||||
|
||||
public function testIsWritable()
|
||||
{
|
||||
$this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'bar', array()));
|
||||
$this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'baz', array()));
|
||||
$this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'parent', array()));
|
||||
$this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'a', array()));
|
||||
$this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'b', array()));
|
||||
$this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'c', array()));
|
||||
$this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'd', array()));
|
||||
$this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'e', array()));
|
||||
$this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'f', array()));
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests\Extractors;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Symfony\Component\PropertyInfo\Extractor\SerializerExtractor;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class SerializerExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var SerializerExtractor
|
||||
*/
|
||||
private $extractor;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->extractor = new SerializerExtractor($classMetadataFactory);
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertEquals(
|
||||
array('collection'),
|
||||
$this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', array('serializer_groups' => array('a')))
|
||||
);
|
||||
}
|
||||
}
|
66
src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
Normal file
66
src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Dummy extends ParentDummy
|
||||
{
|
||||
/**
|
||||
* @var string This is bar.
|
||||
*/
|
||||
private $bar;
|
||||
|
||||
/**
|
||||
* Should be used.
|
||||
*
|
||||
* @var int Should be ignored.
|
||||
*/
|
||||
protected $baz;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
public $bal;
|
||||
|
||||
/**
|
||||
* @var ParentDummy
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* @var \DateTime[]
|
||||
* @Groups({"a", "b"})
|
||||
*/
|
||||
public $collection;
|
||||
|
||||
/**
|
||||
* A.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getA()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* B.
|
||||
*
|
||||
* @param ParentDummy|null $parent
|
||||
*/
|
||||
public function setB(ParentDummy $parent = null)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DummyExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getShortDescription($class, $property, array $context = array())
|
||||
{
|
||||
return 'short';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLongDescription($class, $property, array $context = array())
|
||||
{
|
||||
return 'long';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes($class, $property, array $context = array())
|
||||
{
|
||||
return array(new Type(Type::BUILTIN_TYPE_INT));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($class, $property, array $context = array())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($class, $property, array $context = array())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties($class, array $context = array())
|
||||
{
|
||||
return array('a', 'b');
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ParentDummy
|
||||
{
|
||||
/**
|
||||
* Short description.
|
||||
*
|
||||
* Long description.
|
||||
*/
|
||||
public $foo;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $foo2;
|
||||
|
||||
/**
|
||||
* @var callback
|
||||
*/
|
||||
public $foo3;
|
||||
|
||||
/**
|
||||
* @var void
|
||||
*/
|
||||
public $foo4;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
public $foo5;
|
||||
|
||||
/**
|
||||
* @var \SplFileInfo[]|resource
|
||||
*/
|
||||
public $files;
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isC()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canD()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $e
|
||||
*/
|
||||
public function addE($e)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $f
|
||||
*/
|
||||
public function removeF(\DateTime $f)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Php7Dummy
|
||||
{
|
||||
public function getFoo(): array
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar(int $bar)
|
||||
{
|
||||
}
|
||||
|
||||
public function addBaz(string $baz)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyInfoExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var PropertyInfoExtractor
|
||||
*/
|
||||
private $propertyInfo;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$extractors = array(new DummyExtractor());
|
||||
$this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors);
|
||||
}
|
||||
|
||||
public function testInstanceOf()
|
||||
{
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface', $this->propertyInfo);
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface', $this->propertyInfo);
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface', $this->propertyInfo);
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface', $this->propertyInfo);
|
||||
}
|
||||
|
||||
public function testGetShortDescription()
|
||||
{
|
||||
$this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array()));
|
||||
}
|
||||
|
||||
public function testGetLongDescription()
|
||||
{
|
||||
$this->assertSame('long', $this->propertyInfo->getLongDescription('Foo', 'bar', array()));
|
||||
}
|
||||
|
||||
public function testGetTypes()
|
||||
{
|
||||
$this->assertEquals(array(new Type(Type::BUILTIN_TYPE_INT)), $this->propertyInfo->getTypes('Foo', 'bar', array()));
|
||||
}
|
||||
|
||||
public function testIsReadable()
|
||||
{
|
||||
$this->assertTrue($this->propertyInfo->isReadable('Foo', 'bar', array()));
|
||||
}
|
||||
|
||||
public function testIsWritable()
|
||||
{
|
||||
$this->assertTrue($this->propertyInfo->isWritable('Foo', 'bar', array()));
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertEquals(array('a', 'b'), $this->propertyInfo->getProperties('Foo'));
|
||||
}
|
||||
}
|
47
src/Symfony/Component/PropertyInfo/Tests/TypeTest.php
Normal file
47
src/Symfony/Component/PropertyInfo/Tests/TypeTest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class TypeTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$type = new Type('object', true, 'ArrayObject', true, new Type('int'), new Type('string'));
|
||||
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_OBJECT, $type->getBuiltinType());
|
||||
$this->assertTrue($type->isNullable());
|
||||
$this->assertEquals('ArrayObject', $type->getClassName());
|
||||
$this->assertTrue($type->isCollection());
|
||||
|
||||
$collectionKeyType = $type->getCollectionKeyType();
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyInfo\Type', $collectionKeyType);
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_INT, $collectionKeyType->getBuiltinType());
|
||||
|
||||
$collectionValueType = $type->getCollectionValueType();
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyInfo\Type', $collectionValueType);
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_STRING, $collectionValueType->getBuiltinType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage "foo" is not a valid PHP type.
|
||||
*/
|
||||
public function testInvalidType()
|
||||
{
|
||||
new Type('foo');
|
||||
}
|
||||
}
|
169
src/Symfony/Component/PropertyInfo/Type.php
Normal file
169
src/Symfony/Component/PropertyInfo/Type.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Type value object (immutable).
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
const BUILTIN_TYPE_INT = 'int';
|
||||
const BUILTIN_TYPE_FLOAT = 'float';
|
||||
const BUILTIN_TYPE_STRING = 'string';
|
||||
const BUILTIN_TYPE_BOOL = 'bool';
|
||||
const BUILTIN_TYPE_RESOURCE = 'resource';
|
||||
const BUILTIN_TYPE_OBJECT = 'object';
|
||||
const BUILTIN_TYPE_ARRAY = 'array';
|
||||
const BUILTIN_TYPE_NULL = 'null';
|
||||
const BUILTIN_TYPE_CALLABLE = 'callable';
|
||||
|
||||
/**
|
||||
* List of PHP builtin types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $builtinTypes = array(
|
||||
self::BUILTIN_TYPE_INT,
|
||||
self::BUILTIN_TYPE_FLOAT,
|
||||
self::BUILTIN_TYPE_STRING,
|
||||
self::BUILTIN_TYPE_BOOL,
|
||||
self::BUILTIN_TYPE_RESOURCE,
|
||||
self::BUILTIN_TYPE_OBJECT,
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_CALLABLE,
|
||||
self::BUILTIN_TYPE_NULL,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $builtinType;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $nullable;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
/**
|
||||
* @var Type|null
|
||||
*/
|
||||
private $collectionKeyType;
|
||||
|
||||
/**
|
||||
* @var Type|null
|
||||
*/
|
||||
private $collectionValueType;
|
||||
|
||||
/**
|
||||
* @param string $builtinType
|
||||
* @param bool $nullable
|
||||
* @param string|null $class
|
||||
* @param bool $collection
|
||||
* @param Type|null $collectionKeyType
|
||||
* @param Type|null $collectionValueType
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($builtinType, $nullable = false, $class = null, $collection = false, Type $collectionKeyType = null, Type $collectionValueType = null)
|
||||
{
|
||||
if (!in_array($builtinType, self::$builtinTypes)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType));
|
||||
}
|
||||
|
||||
$this->builtinType = $builtinType;
|
||||
$this->nullable = $nullable;
|
||||
$this->class = $class;
|
||||
$this->collection = $collection;
|
||||
$this->collectionKeyType = $collectionKeyType;
|
||||
$this->collectionValueType = $collectionValueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets built-in type.
|
||||
*
|
||||
* Can be bool, int, float, string, array, object, resource, null or callback.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBuiltinType()
|
||||
{
|
||||
return $this->builtinType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows null value?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNullable()
|
||||
{
|
||||
return $this->nullable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
*
|
||||
* Only applicable if the built-in type is object.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is collection?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCollection()
|
||||
{
|
||||
return $this->collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection key type.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type|null
|
||||
*/
|
||||
public function getCollectionKeyType()
|
||||
{
|
||||
return $this->collectionKeyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection value type.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type|null
|
||||
*/
|
||||
public function getCollectionValueType()
|
||||
{
|
||||
return $this->collectionValueType;
|
||||
}
|
||||
}
|
51
src/Symfony/Component/PropertyInfo/composer.json
Normal file
51
src/Symfony/Component/PropertyInfo/composer.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "symfony/property-info",
|
||||
"type": "library",
|
||||
"description": "Symfony Property Info Component",
|
||||
"keywords": [
|
||||
"property",
|
||||
"type",
|
||||
"PHPDoc",
|
||||
"symfony",
|
||||
"validator",
|
||||
"doctrine"
|
||||
],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kévin Dunglas",
|
||||
"email": "dunglas@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7",
|
||||
"symfony/serializer": "~2.7",
|
||||
"phpdocumentor/reflection": "^1.0.7",
|
||||
"doctrine/annotations": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpdocumentor/reflection": "<1.0.7"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/doctrine-bridge": "To use Doctrine metadata",
|
||||
"phpdocumentor/reflection": "To use the PHPDoc",
|
||||
"symfony/serializer": "To use Serializer metadata"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\PropertyInfo\\": "" }
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
}
|
||||
}
|
29
src/Symfony/Component/PropertyInfo/phpunit.xml.dist
Normal file
29
src/Symfony/Component/PropertyInfo/phpunit.xml.dist
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony Property Info Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
Reference in New Issue
Block a user