Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Make constructor argument override the property docblock in PhpDocExtractor #30056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,7 @@ private function registerPropertyInfoConfiguration(ContainerBuilder $container,
$definition->setPrivate(true);
$definition->addTag('property_info.description_extractor', ['priority' => -1000]);
$definition->addTag('property_info.type_extractor', ['priority' => -1001]);
$definition->addTag('property_info.constructor_extractor', ['priority' => -1001]);
}
}

Expand Down
2 changes: 2 additions & 0 deletions 2 src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass;
use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoConstructorPass;
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass;
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
use Symfony\Component\Serializer\DependencyInjection\SerializerPass;
Expand Down Expand Up @@ -116,6 +117,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new FragmentRendererPass());
$this->addCompilerPassIfExists($container, SerializerPass::class);
$this->addCompilerPassIfExists($container, PropertyInfoPass::class);
$this->addCompilerPassIfExists($container, PropertyInfoConstructorPass::class);
$container->addCompilerPass(new DataCollectorTranslatorPass());
$container->addCompilerPass(new ControllerArgumentValueResolverPass());
$container->addCompilerPass(new CachePoolPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 32);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
<service id="property_info.reflection_extractor" class="Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor">
<tag name="property_info.list_extractor" priority="-1000" />
<tag name="property_info.type_extractor" priority="-1002" />
<tag name="property_info.constructor_extractor" priority="-1002" />
<tag name="property_info.access_extractor" priority="-1000" />
</service>

<service id="property_info.constructor_extractor" class="Symfony\Component\PropertyInfo\Extractor\ConstructorExtractor">
<argument type="collection" />
<tag name="property_info.type_extractor" priority="-999" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\PropertyInfo\Type;

class PropertyInfoTest extends WebTestCase
Expand All @@ -22,6 +23,29 @@ public function testPhpDocPriority()

$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT))], $container->get('test.property_info')->getTypes('Symfony\Bundle\FrameworkBundle\Tests\Functional\Dummy', 'codes'));
}

/**
* @dataProvider constructorOverridesPropertyTypeProvider
*/
public function testConstructorOverridesPropertyType(ContainerInterface $container, $property, array $type = null)
{
$extractor = $container->get('test.property_info');
$this->assertEquals($type, $extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property));
}

public function constructorOverridesPropertyTypeProvider()
{
static::bootKernel(['test_case' => 'Serializer']);
$c = static::$kernel->getContainer();

return [
[$c, 'timezone', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeZone')]],
[$c, 'date', [new Type(Type::BUILTIN_TYPE_INT)]],
[$c, 'dateObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeInterface')]],
[$c, 'dateTime', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')]],
[$c, 'ddd', null],
];
}
}

class Dummy
Expand Down
4 changes: 2 additions & 2 deletions 4 src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"symfony/var-dumper": "~3.3|~4.0",
"symfony/workflow": "~3.3|~4.0",
"symfony/yaml": "~3.2|~4.0",
"symfony/property-info": "~3.3|~4.0",
"symfony/property-info": "~3.4.23|^4.2.4",
"symfony/lock": "~3.4|~4.0",
"symfony/web-link": "~3.3|~4.0",
"doctrine/annotations": "~1.0",
Expand All @@ -66,7 +66,7 @@
"symfony/asset": "<3.3",
"symfony/console": "<3.4",
"symfony/form": "<3.4",
"symfony/property-info": "<3.3",
"symfony/property-info": "<3.4.23|>=4.0.0,<4.2.4",
"symfony/serializer": "<3.3",
"symfony/stopwatch": "<3.4",
"symfony/translation": "<3.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?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\DependencyInjection;

use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* Adds extractors to the property_info.constructor_extractor service.
*
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
*/
class PropertyInfoConstructorPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;

private $service;
private $tag;

public function __construct($service = 'property_info.constructor_extractor', $tag = 'property_info.constructor_extractor')
{
$this->service = $service;
$this->tag = $tag;
}

/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->service)) {
return;
}
$definition = $container->getDefinition($this->service);

$listExtractors = $this->findAndSortTaggedServices($this->tag, $container);
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
}
}
Original file line number Diff line number Diff line change
@@ -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\Extractor;

/**
* Infers the constructor argument type.
*
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
*/
interface ConstructorArgumentTypeExtractorInterface
{
/**
* Gets types of an argument from constructor.
*
* @param string $class
* @param string $property
*
* @return Type[]|null
*/
public function getTypesFromConstructor($class, $property);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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\PropertyTypeExtractorInterface;

/**
* Extracts the constructor argument type using ConstructorArgumentTypeExtractorInterface implementations.
*
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
*/
class ConstructorExtractor implements PropertyTypeExtractorInterface
{
/** @var iterable|ConstructorArgumentTypeExtractorInterface[] */
private $extractors;

/**
* @param iterable|ConstructorArgumentTypeExtractorInterface[] $extractors
*/
public function __construct($extractors = [])
{
$this->extractors = $extractors;
}

/**
* {@inheritdoc}
*/
public function getTypes($class, $property, array $context = [])
{
foreach ($this->extractors as $extractor) {
$value = $extractor->getTypesFromConstructor($class, $property);
if (null !== $value) {
return $value;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*
* @final since version 3.3
*/
class PhpDocExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface
class PhpDocExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface
{
const PROPERTY = 0;
const ACCESSOR = 1;
Expand Down Expand Up @@ -151,6 +151,32 @@ public function getTypes($class, $property, array $context = [])
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
}

/**
* {@inheritdoc}
*/
public function getTypesFromConstructor($class, $property)
{
$docBlock = $this->getDocBlockFromConstructor($class, $property);

if (!$docBlock) {
return;
}

$types = [];
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
foreach ($docBlock->getTagsByName('param') as $tag) {
if ($tag && null !== $tag->getType()) {
$types = array_merge($types, $this->phpDocTypeHelper->getTypes($tag->getType()));
}
}

if (!isset($types[0])) {
return;
}

return $types;
}

/**
* Gets the DocBlock for this property.
*
Expand Down Expand Up @@ -189,6 +215,51 @@ private function getDocBlock($class, $property)
return $this->docBlocks[$propertyHash] = $data;
}

/**
* Gets the DocBlock from a constructor.
*
* @param string $class
* @param string $property
*
* @return DocBlock|null
*/
private function getDocBlockFromConstructor($class, $property)
{
try {
$reflectionClass = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
return null;
}
$reflectionConstructor = $reflectionClass->getConstructor();
if (!$reflectionConstructor) {
return null;
}

try {
$docBlock = $this->docBlockFactory->create($reflectionConstructor, $this->contextFactory->createFromReflector($reflectionConstructor));

return $this->filterDocBlockParams($docBlock, $property);
} catch (\InvalidArgumentException $e) {
return null;
}
}

/**
* @param DocBlock $docBlock
* @param string $allowedParam
*
* @return DocBlock
*/
private function filterDocBlockParams(DocBlock $docBlock, $allowedParam)
{
$tags = array_values(array_filter($docBlock->getTagsByName('param'), function ($tag) use ($allowedParam) {
return $tag instanceof DocBlock\Tags\Param && $allowedParam === $tag->getVariableName();
}));

return new DocBlock($docBlock->getSummary(), $docBlock->getDescription(), $tags, $docBlock->getContext(),
$docBlock->getLocation(), $docBlock->isTemplateStart(), $docBlock->isTemplateEnd());
}

/**
* Gets the DocBlock from a property.
*
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.