[DoctrineAnnotaitonGenerated] Update to doctrine/annotations 1.11 + fix static (#4479)

This commit is contained in:
Tomas Votruba 2020-10-25 14:43:05 +01:00 committed by GitHub
parent 5bb03f2fcb
commit 0f2e6d407e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 675 additions and 378 deletions

View File

@ -49,6 +49,7 @@ expectedArguments(
\Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::IS_FIRST_LEVEL_STATEMENT,
\Rector\NodeTypeResolver\Node\AttributeKey::IS_FRESH_NODE,
);
expectedArguments(
@ -83,6 +84,7 @@ expectedArguments(
\Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::IS_FIRST_LEVEL_STATEMENT,
\Rector\NodeTypeResolver\Node\AttributeKey::IS_FRESH_NODE,
);
expectedArguments(

View File

@ -28,7 +28,7 @@
"php": "^7.2.4|^8.0",
"ext-json": "*",
"composer/xdebug-handler": "^1.4",
"doctrine/annotations": "^1.10.4",
"doctrine/annotations": "^1.11",
"doctrine/inflector": "^1.4|^2.0",
"jean85/pretty-package-versions": "^1.5.1",
"migrify/php-config-printer": "^0.3.45",

View File

@ -1,4 +1,4 @@
# All 597 Rectors Overview
# All 599 Rectors Overview
- [Projects](#projects)
---
@ -8,7 +8,7 @@
- [Architecture](#architecture) (2)
- [Autodiscovery](#autodiscovery) (4)
- [CakePHP](#cakephp) (6)
- [CodeQuality](#codequality) (60)
- [CodeQuality](#codequality) (61)
- [CodingStyle](#codingstyle) (33)
- [DeadCode](#deadcode) (41)
- [Decouple](#decouple) (1)
@ -71,7 +71,7 @@
- [Symfony](#symfony) (34)
- [SymfonyCodeQuality](#symfonycodequality) (1)
- [SymfonyPHPUnit](#symfonyphpunit) (1)
- [SymfonyPhpConfig](#symfonyphpconfig) (2)
- [SymfonyPhpConfig](#symfonyphpconfig) (3)
- [Transform](#transform) (11)
- [Twig](#twig) (1)
- [TypeDeclaration](#typedeclaration) (9)
@ -1123,6 +1123,23 @@ Change OR, AND to ||, && with more common understanding
<br><br>
### `MoveOutMethodCallInsideIfConditionRector`
- class: [`Rector\CodeQuality\Rector\If_\MoveOutMethodCallInsideIfConditionRector`](/rules/code-quality/src/Rector/If_/MoveOutMethodCallInsideIfConditionRector.php)
- [test fixtures](/rules/code-quality/tests/Rector/If_/MoveOutMethodCallInsideIfConditionRector/Fixture)
Move out method call inside If condition
```diff
-if ($obj->run($arg) === 1) {
+$objRun = $obj->run($arg);
+if ($objRun === 1) {
}
```
<br><br>
### `NewStaticToNewSelfRector`
- class: [`Rector\CodeQuality\Rector\New_\NewStaticToNewSelfRector`](/rules/code-quality/src/Rector/New_/NewStaticToNewSelfRector.php)
@ -15484,6 +15501,29 @@ Move self::$container service fetching from test methods up to setUp method
## SymfonyPhpConfig
### `AutoInPhpSymfonyConfigRector`
- class: [`Rector\SymfonyPhpConfig\Rector\MethodCall\AutoInPhpSymfonyConfigRector`](/rules/symfony-php-config/src/Rector/MethodCall/AutoInPhpSymfonyConfigRector.php)
- [test fixtures](/rules/symfony-php-config/tests/Rector/MethodCall/AutoInPhpSymfonyConfigRector/Fixture)
Make sure there is public(), autowire(), `autoconfigure()` calls on `defaults()` in Symfony configs
```diff
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
- ->autowire();
+ ->autowire()
+ ->public()
+ ->autoconfigure();
};
```
<br><br>
### `ChangeServiceArgumentsToMethodCallRector`
- class: [`Rector\SymfonyPhpConfig\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector`](/rules/symfony-php-config/src/Rector/MethodCall/ChangeServiceArgumentsToMethodCallRector.php)

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\AttributeAwarePhpDoc\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Ast']);

View File

@ -14,7 +14,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\BetterPhpDocParser\\', __DIR__ . '/../src')
->exclude([

View File

@ -26,7 +26,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\Caching\\', __DIR__ . '/../src');

View File

@ -18,7 +18,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\ConsoleDiffer\\', __DIR__ . '/../src');

View File

@ -1,22 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Rector\DoctrineAnnotationGenerated;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
@ -24,126 +7,35 @@ use Doctrine\Common\Annotations\Annotation\Target;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use function array_merge;
use function class_exists;
use function extension_loaded;
use function ini_get;
/**
* A reader for docblock annotations.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations\Reader
{
/**
* Global map for imports.
*
* @var array
* @var array<string, class-string>
*/
private static $globalImports = ['ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation'];
private static $globalImports = ['ignoreannotation' => \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation::class];
/**
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
* The names are case sensitive.
*
* @var array
* @var array<string, true>
*/
private static $globalIgnoredNames = [
// Annotation tags
'Annotation' => true,
'Attribute' => true,
'Attributes' => true,
/* Can we enable this? 'Enum' => true, */
'Required' => true,
'Target' => true,
// Widely used tags (but not existent in phpdoc)
'fix' => true,
'fixme' => true,
'override' => true,
// PHPDocumentor 1 tags
'abstract' => true,
'access' => true,
'code' => true,
'deprec' => true,
'endcode' => true,
'exception' => true,
'final' => true,
'ingroup' => true,
'inheritdoc' => true,
'inheritDoc' => true,
'magic' => true,
'name' => true,
'toc' => true,
'tutorial' => true,
'private' => true,
'static' => true,
'staticvar' => true,
'staticVar' => true,
'throw' => true,
// PHPDocumentor 2 tags.
'api' => true,
'author' => true,
'category' => true,
'copyright' => true,
'deprecated' => true,
'example' => true,
'filesource' => true,
'global' => true,
'ignore' => true,
/* Can we enable this? 'index' => true, */
'internal' => true,
'license' => true,
'link' => true,
'method' => true,
'package' => true,
'param' => true,
'property' => true,
'property-read' => true,
'property-write' => true,
'return' => true,
'see' => true,
'since' => true,
'source' => true,
'subpackage' => true,
'throws' => true,
'todo' => true,
'TODO' => true,
'usedby' => true,
'uses' => true,
'var' => true,
'version' => true,
// PHPUnit tags
'codeCoverageIgnore' => true,
'codeCoverageIgnoreStart' => true,
'codeCoverageIgnoreEnd' => true,
// PHPCheckStyle
'SuppressWarnings' => true,
// PHPStorm
'noinspection' => true,
// PEAR
'package_version' => true,
// PlantUML
'startuml' => true,
'enduml' => true,
// Symfony 3.3 Cache Adapter
'experimental' => true,
// Slevomat Coding Standard
'phpcsSuppress' => true,
// PHP CodeSniffer
'codingStandardsIgnoreStart' => true,
'codingStandardsIgnoreEnd' => true,
// PHPStan
'template' => true,
'implements' => true,
'extends' => true,
'use' => true,
];
private static $globalIgnoredNames = \Doctrine\Common\Annotations\ImplicitlyIgnoredAnnotationNames::LIST;
/**
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
* The names are case sensitive.
*
* @var array
* @var array<string, true>
*/
private static $globalIgnoredNamespaces = [];
/**
@ -167,52 +59,48 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
/**
* Annotations parser.
*
* @var \Doctrine\Common\Annotations\DocParser
* @var DocParser
*/
private $parser;
/**
* Annotations parser used to collect parsing metadata.
*
* @var \Doctrine\Common\Annotations\DocParser
* @var DocParser
*/
private $preParser;
/**
* PHP parser used to collect imports.
*
* @var \Doctrine\Common\Annotations\PhpParser
* @var PhpParser
*/
private $phpParser;
/**
* In-memory cache mechanism to store imported annotations per class.
*
* @var array
* @var array<string, array<string, class-string>>
*/
private $imports = [];
/**
* In-memory cache mechanism to store ignored annotations per class.
*
* @var array
* @var array<string, array<string, true>>
*/
private $ignoredAnnotationNames = [];
/**
* Constructor.
*
* Initializes a new AnnotationReader.
*
* @param DocParser $parser
*
* @throws AnnotationException
*/
public function __construct(\Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser $parser = null)
{
if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) {
if (\extension_loaded('Zend Optimizer+') && (\ini_get('zend_optimizerplus.save_comments') === '0' || \ini_get('opcache.save_comments') === '0')) {
throw \Doctrine\Common\Annotations\AnnotationException::optimizerPlusSaveComments();
}
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) {
if (\extension_loaded('Zend OPcache') && \ini_get('opcache.save_comments') === 0) {
throw \Doctrine\Common\Annotations\AnnotationException::optimizerPlusSaveComments();
}
// Make sure that the IgnoreAnnotation annotation is loaded
class_exists(\Doctrine\Common\Annotations\Annotation\IgnoreAnnotation::class);
\class_exists(\Doctrine\Common\Annotations\Annotation\IgnoreAnnotation::class);
$this->parser = $parser ?: new \Doctrine\Common\Annotations\DocParser();
$this->preParser = new \Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser();
$this->preParser->setImports(self::$globalImports);
@ -250,7 +138,7 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
public function getPropertyAnnotations(\ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$context = 'property ' . $class->getName() . "::\$" . $property->getName();
$context = 'property ' . $class->getName() . '::$' . $property->getName();
$this->parser->setTarget(\Doctrine\Common\Annotations\Annotation\Target::TARGET_PROPERTY);
$this->parser->setImports($this->getPropertyImports($property));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
@ -299,9 +187,7 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
/**
* Returns the ignored annotations for the given class.
*
* @param \ReflectionClass $class
*
* @return array
* @return array<string, true>
*/
private function getIgnoredAnnotationNames(\ReflectionClass $class)
{
@ -315,9 +201,7 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
/**
* Retrieves imports.
*
* @param \ReflectionClass $class
*
* @return array
* @return array<string, class-string>
*/
private function getClassImports(\ReflectionClass $class)
{
@ -331,9 +215,7 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
/**
* Retrieves imports for methods.
*
* @param \ReflectionMethod $method
*
* @return array
* @return array<string, class-string>
*/
private function getMethodImports(\ReflectionMethod $method)
{
@ -341,18 +223,17 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
$classImports = $this->getClassImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) {
if ($trait->hasMethod($method->getName()) && $trait->getFileName() === $method->getFileName()) {
$traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
if (!$trait->hasMethod($method->getName()) || $trait->getFileName() !== $method->getFileName()) {
continue;
}
$traitImports = \array_merge($traitImports, $this->phpParser->parseClass($trait));
}
return array_merge($classImports, $traitImports);
return \array_merge($classImports, $traitImports);
}
/**
* Retrieves imports for properties.
*
* @param \ReflectionProperty $property
*
* @return array
* @return array<string, class-string>
*/
private function getPropertyImports(\ReflectionProperty $property)
{
@ -360,30 +241,30 @@ class ConstantPreservingAnnotationReader implements \Doctrine\Common\Annotations
$classImports = $this->getClassImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) {
if ($trait->hasProperty($property->getName())) {
$traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
if (!$trait->hasProperty($property->getName())) {
continue;
}
$traitImports = \array_merge($traitImports, $this->phpParser->parseClass($trait));
}
return array_merge($classImports, $traitImports);
return \array_merge($classImports, $traitImports);
}
/**
* Collects parsing metadata for a given class.
*
* @param \ReflectionClass $class
*/
private function collectParsingMetadata(\ReflectionClass $class)
{
$ignoredAnnotationNames = self::$globalIgnoredNames;
$annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name);
foreach ($annotations as $annotation) {
if ($annotation instanceof \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation) {
foreach ($annotation->names as $annot) {
$ignoredAnnotationNames[$annot] = true;
}
if (!$annotation instanceof \Doctrine\Common\Annotations\Annotation\IgnoreAnnotation) {
continue;
}
foreach ($annotation->names as $annot) {
$ignoredAnnotationNames[$annot] = true;
}
}
$name = $class->getName();
$this->imports[$name] = array_merge(self::$globalImports, $this->phpParser->parseClass($class), ['__NAMESPACE__' => $class->getNamespaceName()]);
$this->imports[$name] = \array_merge(self::$globalImports, $this->phpParser->parseClass($class), ['__NAMESPACE__' => $class->getNamespaceName(), 'self' => $name]);
$this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
}
}

View File

@ -1,51 +1,54 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Rector\DoctrineAnnotationGenerated;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\Enum;
use Doctrine\Common\Annotations\Annotation\Target;
use Doctrine\Common\Annotations\Annotation\Attributes;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
use RuntimeException;
use stdClass;
use function array_keys;
use function array_map;
use function class_exists;
use function constant;
use function defined;
use function explode;
use function gettype;
use function implode;
use function in_array;
use function interface_exists;
use function is_array;
use function is_object;
use function is_subclass_of;
use function json_encode;
use function ltrim;
use function preg_match;
use function reset;
use function rtrim;
use function sprintf;
use function stripos;
use function strlen;
use function strpos;
use function strrpos;
use function strtolower;
use function substr;
use function trim;
use const PHP_VERSION_ID;
/**
* A parser for docblock annotations.
*
* It is strongly discouraged to change the default annotation parsing process.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
final class ConstantPreservingDocParser
{
/**
* An array of all valid tokens for a class name.
*
* @var array
* @phpstan-var list<int>
*/
private static $classIdentifiers = [\Doctrine\Common\Annotations\DocLexer::T_IDENTIFIER, \Doctrine\Common\Annotations\DocLexer::T_TRUE, \Doctrine\Common\Annotations\DocLexer::T_FALSE, \Doctrine\Common\Annotations\DocLexer::T_NULL];
/**
@ -57,7 +60,7 @@ final class ConstantPreservingDocParser
/**
* Current target context.
*
* @var integer
* @var int
*/
private $target;
/**
@ -69,27 +72,27 @@ final class ConstantPreservingDocParser
/**
* Flag to control if the current annotation is nested or not.
*
* @var boolean
* @var bool
*/
private $isNestedAnnotation = false;
/**
* Hashmap containing all use-statements that are to be used when parsing
* the given doc block.
*
* @var array
* @var array<string, class-string>
*/
private $imports = [];
/**
* This hashmap is used internally to cache results of class_exists()
* look-ups.
*
* @var array
* @var array<class-string, bool>
*/
private $classExists = [];
/**
* Whether annotations that have not been imported should be ignored.
*
* @var boolean
* @var bool
*/
private $ignoreNotImportedAnnotations = false;
/**
@ -102,7 +105,6 @@ final class ConstantPreservingDocParser
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
* The names must be the raw names as used in the class, not the fully qualified
* class names.
*
* @var bool[] indexed by annotation name
*/
@ -114,20 +116,18 @@ final class ConstantPreservingDocParser
* @var bool[] indexed by namespace name
*/
private $ignoredAnnotationNamespaces = [];
/**
* @var string
*/
/** @var string */
private $context = '';
/**
* Hash-map for caching annotation metadata.
*
* @var array
* @var array<class-string, mixed[]>
*/
private static $annotationMetadata = ['Doctrine\Common\Annotations\Annotation\Target' => ['is_annotation' => true, 'has_constructor' => true, 'properties' => [], 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_CLASS, 'default_property' => 'value', 'attribute_types' => ['value' => ['required' => false, 'type' => 'array', 'array_type' => 'string', 'value' => 'array<string>']]], 'Doctrine\Common\Annotations\Annotation\Attribute' => ['is_annotation' => true, 'has_constructor' => false, 'targets_literal' => 'ANNOTATION_ANNOTATION', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_ANNOTATION, 'default_property' => 'name', 'properties' => ['name' => 'name', 'type' => 'type', 'required' => 'required'], 'attribute_types' => ['value' => ['required' => true, 'type' => 'string', 'value' => 'string'], 'type' => ['required' => true, 'type' => 'string', 'value' => 'string'], 'required' => ['required' => false, 'type' => 'boolean', 'value' => 'boolean']]], 'Doctrine\Common\Annotations\Annotation\Attributes' => ['is_annotation' => true, 'has_constructor' => false, 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_CLASS, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => ['value' => ['type' => 'array', 'required' => true, 'array_type' => 'Doctrine\Common\Annotations\Annotation\Attribute', 'value' => 'array<Doctrine\Common\Annotations\Annotation\Attribute>']]], 'Doctrine\Common\Annotations\Annotation\Enum' => ['is_annotation' => true, 'has_constructor' => true, 'targets_literal' => 'ANNOTATION_PROPERTY', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_PROPERTY, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => ['value' => ['type' => 'array', 'required' => true], 'literal' => ['type' => 'array', 'required' => false]]]];
private static $annotationMetadata = [\Doctrine\Common\Annotations\Annotation\Target::class => ['is_annotation' => true, 'has_constructor' => true, 'properties' => [], 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_CLASS, 'default_property' => 'value', 'attribute_types' => ['value' => ['required' => false, 'type' => 'array', 'array_type' => 'string', 'value' => 'array<string>']]], \Doctrine\Common\Annotations\Annotation\Attribute::class => ['is_annotation' => true, 'has_constructor' => false, 'targets_literal' => 'ANNOTATION_ANNOTATION', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_ANNOTATION, 'default_property' => 'name', 'properties' => ['name' => 'name', 'type' => 'type', 'required' => 'required'], 'attribute_types' => ['value' => ['required' => true, 'type' => 'string', 'value' => 'string'], 'type' => ['required' => true, 'type' => 'string', 'value' => 'string'], 'required' => ['required' => false, 'type' => 'boolean', 'value' => 'boolean']]], \Doctrine\Common\Annotations\Annotation\Attributes::class => ['is_annotation' => true, 'has_constructor' => false, 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_CLASS, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => ['value' => ['type' => 'array', 'required' => true, 'array_type' => \Doctrine\Common\Annotations\Annotation\Attribute::class, 'value' => 'array<' . \Doctrine\Common\Annotations\Annotation\Attribute::class . '>']]], \Doctrine\Common\Annotations\Annotation\Enum::class => ['is_annotation' => true, 'has_constructor' => true, 'targets_literal' => 'ANNOTATION_PROPERTY', 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_PROPERTY, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => ['value' => ['type' => 'array', 'required' => true], 'literal' => ['type' => 'array', 'required' => false]]]];
/**
* Hash-map for handle types declaration.
*
* @var array
* @var array<string, string>
*/
private static $typeMap = [
'float' => 'double',
@ -171,7 +171,7 @@ final class ConstantPreservingDocParser
/**
* Sets ignore on not-imported annotations.
*
* @param boolean $bool
* @param bool $bool
*
* @return void
*/
@ -185,6 +185,7 @@ final class ConstantPreservingDocParser
* @param string $namespace
*
* @return void
*
* @throws RuntimeException
*/
public function addNamespace($namespace)
@ -197,9 +198,10 @@ final class ConstantPreservingDocParser
/**
* Sets the imports.
*
* @param array $imports
* @param array<string, class-string> $imports
*
* @return void
*
* @throws RuntimeException
*/
public function setImports(array $imports)
@ -212,7 +214,7 @@ final class ConstantPreservingDocParser
/**
* Sets current target context as bitmask.
*
* @param integer $target
* @param int $target
*
* @return void
*/
@ -226,9 +228,10 @@ final class ConstantPreservingDocParser
* @param string $input The docblock string to parse.
* @param string $context The parsing context.
*
* @return array Array of annotations. If no annotations are found, an empty array is returned.
* @throws AnnotationException
* @throws ReflectionException
*
* @phpstan-return list<object> Array of annotations. If no annotations are found, an empty array is returned.
*/
public function parse($input, $context = '')
{
@ -237,7 +240,7 @@ final class ConstantPreservingDocParser
return [];
}
$this->context = $context;
$this->lexer->setInput(trim(substr($input, $pos), '* /'));
$this->lexer->setInput(\trim(\substr($input, $pos), '* /'));
$this->lexer->moveNext();
return $this->Annotations();
}
@ -250,8 +253,8 @@ final class ConstantPreservingDocParser
{
$pos = 0;
// search for first valid annotation
while (($pos = strpos($input, '@', $pos)) !== false) {
$preceding = substr($input, $pos - 1, 1);
while (($pos = \strpos($input, '@', $pos)) !== false) {
$preceding = \substr($input, $pos - 1, 1);
// if the @ is preceded by a space, a tab or * it is valid
if ($pos === 0 || $preceding === ' ' || $preceding === '*' || $preceding === "\t") {
return $pos;
@ -267,6 +270,7 @@ final class ConstantPreservingDocParser
* @param int $token Type of token.
*
* @return bool True if tokens match; false otherwise.
*
* @throws AnnotationException
*/
private function match(int $token): bool
@ -283,28 +287,30 @@ final class ConstantPreservingDocParser
* a syntax error is raised.
*
* @throws AnnotationException
*
* @phpstan-param list<mixed[]> $tokens
*/
private function matchAny(array $tokens): bool
{
if (!$this->lexer->isNextTokenAny($tokens)) {
throw $this->syntaxError(implode(' or ', array_map([$this->lexer, 'getLiteral'], $tokens)));
throw $this->syntaxError(\implode(' or ', \array_map([$this->lexer, 'getLiteral'], $tokens)));
}
return $this->lexer->moveNext();
}
/**
* Generates a new syntax error.
*
* @param string $expected Expected string.
* @param array|null $token Optional token.
* @param string $expected Expected string.
* @param mixed[]|null $token Optional token.
*/
private function syntaxError(string $expected, ?array $token = null): \Doctrine\Common\Annotations\AnnotationException
{
if ($token === null) {
$token = $this->lexer->lookahead;
}
$message = sprintf('Expected %s, got ', $expected);
$message .= $this->lexer->lookahead === null ? 'end of string' : sprintf("'%s' at position %s", $token['value'], $token['position']);
if (strlen($this->context)) {
$message = \sprintf('Expected %s, got ', $expected);
$message .= $this->lexer->lookahead === null ? 'end of string' : \sprintf("'%s' at position %s", $token['value'], $token['position']);
if (\strlen($this->context)) {
$message .= ' in ' . $this->context;
}
$message .= '.';
@ -313,6 +319,8 @@ final class ConstantPreservingDocParser
/**
* Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism
* but uses the {@link AnnotationRegistry} to load classes.
*
* @param class-string $fqcn
*/
private function classExists(string $fqcn): bool
{
@ -320,7 +328,7 @@ final class ConstantPreservingDocParser
return $this->classExists[$fqcn];
}
// first check if the class already exists, maybe loaded through another AnnotationReader
if (class_exists($fqcn, false)) {
if (\class_exists($fqcn, false)) {
return $this->classExists[$fqcn] = true;
}
// final check, does this class exist?
@ -329,7 +337,7 @@ final class ConstantPreservingDocParser
/**
* Collects parsing metadata for a given annotation class
*
* @param string $name The annotation name
* @param class-string $name The annotation name
*
* @throws AnnotationException
* @throws ReflectionException
@ -340,17 +348,23 @@ final class ConstantPreservingDocParser
self::$metadataParser = new self();
self::$metadataParser->setIgnoreNotImportedAnnotations(true);
self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames);
self::$metadataParser->setImports(['enum' => 'Doctrine\Common\Annotations\Annotation\Enum', 'target' => 'Doctrine\Common\Annotations\Annotation\Target', 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute', 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes']);
self::$metadataParser->setImports(['enum' => \Doctrine\Common\Annotations\Annotation\Enum::class, 'target' => \Doctrine\Common\Annotations\Annotation\Target::class, 'attribute' => \Doctrine\Common\Annotations\Annotation\Attribute::class, 'attributes' => \Doctrine\Common\Annotations\Annotation\Attributes::class]);
// Make sure that annotations from metadata are loaded
class_exists(\Doctrine\Common\Annotations\Annotation\Enum::class);
class_exists(\Doctrine\Common\Annotations\Annotation\Target::class);
class_exists(\Doctrine\Common\Annotations\Annotation\Attribute::class);
class_exists(\Doctrine\Common\Annotations\Annotation\Attributes::class);
\class_exists(\Doctrine\Common\Annotations\Annotation\Enum::class);
\class_exists(\Doctrine\Common\Annotations\Annotation\Target::class);
\class_exists(\Doctrine\Common\Annotations\Annotation\Attribute::class);
\class_exists(\Doctrine\Common\Annotations\Annotation\Attributes::class);
}
$class = new \ReflectionClass($name);
$docComment = $class->getDocComment();
// Sets default values for annotation metadata
$metadata = ['default_property' => null, 'has_constructor' => null !== ($constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0, 'properties' => [], 'property_types' => [], 'attribute_types' => [], 'targets_literal' => null, 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_ALL, 'is_annotation' => false !== strpos($docComment, '@Annotation')];
$constructor = $class->getConstructor();
$metadata = ['default_property' => null, 'has_constructor' => $constructor !== null && $constructor->getNumberOfParameters() > 0, 'constructor_args' => [], 'properties' => [], 'property_types' => [], 'attribute_types' => [], 'targets_literal' => null, 'targets' => \Doctrine\Common\Annotations\Annotation\Target::TARGET_ALL, 'is_annotation' => \strpos($docComment, '@Annotation') !== false];
if (\PHP_VERSION_ID < 80000 && $class->implementsInterface(\Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation::class)) {
foreach ($constructor->getParameters() as $parameter) {
$metadata['constructor_args'][$parameter->getName()] = ['position' => $parameter->getPosition(), 'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null];
}
}
// verify that the class is really meant to be an annotation
if ($metadata['is_annotation']) {
self::$metadataParser->setTarget(\Doctrine\Common\Annotations\Annotation\Target::TARGET_CLASS);
@ -360,75 +374,81 @@ final class ConstantPreservingDocParser
$metadata['targets_literal'] = $annotation->literal;
continue;
}
if ($annotation instanceof \Doctrine\Common\Annotations\Annotation\Attributes) {
foreach ($annotation->value as $attribute) {
$this->collectAttributeTypeMetadata($metadata, $attribute);
}
if (!$annotation instanceof \Doctrine\Common\Annotations\Annotation\Attributes) {
continue;
}
foreach ($annotation->value as $attribute) {
$this->collectAttributeTypeMetadata($metadata, $attribute);
}
}
// if not has a constructor will inject values into public properties
if (false === $metadata['has_constructor']) {
if ($metadata['has_constructor'] === false) {
// collect all public properties
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$metadata['properties'][$property->name] = $property->name;
if (false === ($propertyComment = $property->getDocComment())) {
$propertyComment = $property->getDocComment();
if ($propertyComment === false) {
continue;
}
$attribute = new \Doctrine\Common\Annotations\Annotation\Attribute();
$attribute->required = false !== strpos($propertyComment, '@Required');
$attribute->required = \strpos($propertyComment, '@Required') !== false;
$attribute->name = $property->name;
$attribute->type = false !== strpos($propertyComment, '@var') && preg_match('/@var\s+([^\s]+)/', $propertyComment, $matches) ? $matches[1] : 'mixed';
$attribute->type = \strpos($propertyComment, '@var') !== false && \preg_match('/@var\s+([^\s]+)/', $propertyComment, $matches) ? $matches[1] : 'mixed';
$this->collectAttributeTypeMetadata($metadata, $attribute);
// checks if the property has @Enum
if (false !== strpos($propertyComment, '@Enum')) {
$context = 'property ' . $class->name . "::\$" . $property->name;
self::$metadataParser->setTarget(\Doctrine\Common\Annotations\Annotation\Target::TARGET_PROPERTY);
foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
if (!$annotation instanceof \Doctrine\Common\Annotations\Annotation\Enum) {
continue;
}
$metadata['enum'][$property->name]['value'] = $annotation->value;
$metadata['enum'][$property->name]['literal'] = !empty($annotation->literal) ? $annotation->literal : $annotation->value;
if (\strpos($propertyComment, '@Enum') === false) {
continue;
}
$context = 'property ' . $class->name . '::$' . $property->name;
self::$metadataParser->setTarget(\Doctrine\Common\Annotations\Annotation\Target::TARGET_PROPERTY);
foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
if (!$annotation instanceof \Doctrine\Common\Annotations\Annotation\Enum) {
continue;
}
$metadata['enum'][$property->name]['value'] = $annotation->value;
$metadata['enum'][$property->name]['literal'] = !empty($annotation->literal) ? $annotation->literal : $annotation->value;
}
}
// choose the first property as default property
$metadata['default_property'] = reset($metadata['properties']);
$metadata['default_property'] = \reset($metadata['properties']);
}
}
self::$annotationMetadata[$name] = $metadata;
}
/**
* Collects parsing metadata for a given attribute.
*
* @param mixed[] $metadata
*/
private function collectAttributeTypeMetadata(array &$metadata, \Doctrine\Common\Annotations\Annotation\Attribute $attribute): void
{
// handle internal type declaration
$type = self::$typeMap[$attribute->type] ?? $attribute->type;
// handle the case if the property type is mixed
if ('mixed' === $type) {
if ($type === 'mixed') {
return;
}
// Evaluate type
switch (true) {
$pos = \strpos($type, '<');
if ($pos !== false) {
// Checks if the property has array<type>
case false !== ($pos = strpos($type, '<')):
$arrayType = substr($type, $pos + 1, -1);
$type = 'array';
if (isset(self::$typeMap[$arrayType])) {
$arrayType = self::$typeMap[$arrayType];
}
$metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
break;
$arrayType = \substr($type, $pos + 1, -1);
$type = 'array';
if (isset(self::$typeMap[$arrayType])) {
$arrayType = self::$typeMap[$arrayType];
}
$metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
} else {
// Checks if the property has type[]
case false !== ($pos = strrpos($type, '[')):
$arrayType = substr($type, 0, $pos);
$pos = \strrpos($type, '[');
if ($pos !== false) {
$arrayType = \substr($type, 0, $pos);
$type = 'array';
if (isset(self::$typeMap[$arrayType])) {
$arrayType = self::$typeMap[$arrayType];
}
$metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
break;
}
}
$metadata['attribute_types'][$attribute->name]['type'] = $type;
$metadata['attribute_types'][$attribute->name]['value'] = $attribute->type;
@ -439,30 +459,35 @@ final class ConstantPreservingDocParser
*
* @throws AnnotationException
* @throws ReflectionException
*
* @phpstan-return list<object>
*/
private function Annotations(): array
{
$annotations = [];
while (null !== $this->lexer->lookahead) {
if (\Doctrine\Common\Annotations\DocLexer::T_AT !== $this->lexer->lookahead['type']) {
while ($this->lexer->lookahead !== null) {
if ($this->lexer->lookahead['type'] !== \Doctrine\Common\Annotations\DocLexer::T_AT) {
$this->lexer->moveNext();
continue;
}
// make sure the @ is preceded by non-catchable pattern
if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) {
if ($this->lexer->token !== null && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + \strlen($this->lexer->token['value'])) {
$this->lexer->moveNext();
continue;
}
// make sure the @ is followed by either a namespace separator, or
// an identifier token
if (null === ($peek = $this->lexer->glimpse()) || \Doctrine\Common\Annotations\DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true) || $peek['position'] !== $this->lexer->lookahead['position'] + 1) {
$peek = $this->lexer->glimpse();
if ($peek === null || $peek['type'] !== \Doctrine\Common\Annotations\DocLexer::T_NAMESPACE_SEPARATOR && !\in_array($peek['type'], self::$classIdentifiers, true) || $peek['position'] !== $this->lexer->lookahead['position'] + 1) {
$this->lexer->moveNext();
continue;
}
$this->isNestedAnnotation = false;
if (false !== ($annot = $this->Annotation())) {
$annotations[] = $annot;
$annot = $this->Annotation();
if ($annot === false) {
continue;
}
$annotations[] = $annot;
}
return $annotations;
}
@ -473,7 +498,7 @@ final class ConstantPreservingDocParser
* NameSpacePart ::= identifier | null | false | true
* SimpleName ::= identifier | null | false | true
*
* @return mixed False if it is not a valid annotation.
* @return object|false False if it is not a valid annotation.
*
* @throws AnnotationException
* @throws ReflectionException
@ -490,11 +515,11 @@ final class ConstantPreservingDocParser
// only process names which are not fully qualified, yet
// fully qualified names must start with a \
$originalName = $name;
if ('\\' !== $name[0]) {
$pos = strpos($name, '\\');
$alias = false === $pos ? $name : substr($name, 0, $pos);
if ($name[0] !== '\\') {
$pos = \strpos($name, '\\');
$alias = $pos === false ? $name : \substr($name, 0, $pos);
$found = false;
$loweredAlias = strtolower($alias);
$loweredAlias = \strtolower($alias);
if ($this->namespaces) {
foreach ($this->namespaces as $namespace) {
if ($this->classExists($namespace . '\\' . $name)) {
@ -504,8 +529,8 @@ final class ConstantPreservingDocParser
}
}
} elseif (isset($this->imports[$loweredAlias])) {
$namespace = ltrim($this->imports[$loweredAlias], '\\');
$name = false !== $pos ? $namespace . substr($name, $pos) : $namespace;
$namespace = \ltrim($this->imports[$loweredAlias], '\\');
$name = $pos !== false ? $namespace . \substr($name, $pos) : $namespace;
$found = $this->classExists($name);
} elseif (!isset($this->ignoredAnnotationNames[$name]) && isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name)) {
$name = $this->imports['__NAMESPACE__'] . '\\' . $name;
@ -517,12 +542,15 @@ final class ConstantPreservingDocParser
if ($this->isIgnoredAnnotation($name)) {
return false;
}
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context));
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(\sprintf(<<<'EXCEPTION'
The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?
EXCEPTION
, $name, $this->context));
}
}
$name = ltrim($name, '\\');
$name = \ltrim($name, '\\');
if (!$this->classExists($name)) {
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context));
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(\sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context));
}
// at this point, $name contains the fully qualified class name of the
// annotation, and it is also guaranteed that this class exists, and
@ -536,22 +564,31 @@ final class ConstantPreservingDocParser
if ($this->isIgnoredAnnotation($originalName) || $this->isIgnoredAnnotation($name)) {
return false;
}
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context));
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(\sprintf(<<<'EXCEPTION'
The class "%s" is not annotated with @Annotation.
Are you sure this class can be used as annotation?
If so, then you need to add @Annotation to the _class_ doc comment of "%s".
If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.
EXCEPTION
, $name, $name, $originalName, $this->context));
}
//if target is nested annotation
$target = $this->isNestedAnnotation ? \Doctrine\Common\Annotations\Annotation\Target::TARGET_ANNOTATION : $this->target;
// Next will be nested
$this->isNestedAnnotation = true;
//if annotation does not support current target
if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) {
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.', $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal']));
if ((self::$annotationMetadata[$name]['targets'] & $target) === 0 && $target) {
throw \Doctrine\Common\Annotations\AnnotationException::semanticalError(\sprintf(<<<'EXCEPTION'
Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.
EXCEPTION
, $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal']));
}
$values = $this->MethodCall();
if (isset(self::$annotationMetadata[$name]['enum'])) {
// checks all declared attributes
foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {
// checks if the attribute is a valid enumerator
if (isset($values[$property]) && !in_array($values[$property], $enum['value'])) {
if (isset($values[$property]) && !\in_array($values[$property], $enum['value'])) {
throw \Doctrine\Common\Annotations\AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]);
}
}
@ -570,21 +607,42 @@ final class ConstantPreservingDocParser
}
if ($type['type'] === 'array') {
// handle the case of a single value
if (!is_array($values[$property])) {
if (!\is_array($values[$property])) {
$values[$property] = [$values[$property]];
}
// checks if the attribute has array type declaration, such as "array<string>"
if (isset($type['array_type'])) {
foreach ($values[$property] as $item) {
if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) {
if (\gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) {
throw \Doctrine\Common\Annotations\AnnotationException::attributeTypeError($property, $originalName, $this->context, 'either a(n) ' . $type['array_type'] . ', or an array of ' . $type['array_type'] . 's', $item);
}
}
}
} elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) {
} elseif (\gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) {
throw \Doctrine\Common\Annotations\AnnotationException::attributeTypeError($property, $originalName, $this->context, 'a(n) ' . $type['value'], $values[$property]);
}
}
if (\is_subclass_of($name, \Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation::class)) {
if (\PHP_VERSION_ID >= 80000) {
return new $name(...$values);
}
$positionalValues = [];
foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) {
$positionalValues[$parameter['position']] = $parameter['default'];
}
foreach ($values as $property => $value) {
if (!isset(self::$annotationMetadata[$name]['constructor_args'][$property])) {
throw \Doctrine\Common\Annotations\AnnotationException::creationError(\sprintf(<<<'EXCEPTION'
The annotation @%s declared on %s does not have a property named "%s"
that can be set through its named arguments constructor.
Available named arguments: %s
EXCEPTION
, $originalName, $this->context, $property, \implode(', ', \array_keys(self::$annotationMetadata[$name]['constructor_args']))));
}
$positionalValues[self::$annotationMetadata[$name]['constructor_args'][$property]['position']] = $value;
}
return new $name(...$positionalValues);
}
// check if the annotation expects values via the constructor,
// or directly injected into public properties
if (self::$annotationMetadata[$name]['has_constructor'] === true) {
@ -593,12 +651,17 @@ final class ConstantPreservingDocParser
$instance = new $name();
foreach ($values as $property => $value) {
if (!isset(self::$annotationMetadata[$name]['properties'][$property])) {
if ('value' !== $property) {
throw \Doctrine\Common\Annotations\AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties'])));
if ($property !== 'value') {
throw \Doctrine\Common\Annotations\AnnotationException::creationError(\sprintf(<<<'EXCEPTION'
The annotation @%s declared on %s does not have a property named "%s".
Available properties: %s
EXCEPTION
, $originalName, $this->context, $property, \implode(', ', self::$annotationMetadata[$name]['properties'])));
}
// handle the case if the property has no annotations
if (!($property = self::$annotationMetadata[$name]['default_property'])) {
throw \Doctrine\Common\Annotations\AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values)));
$property = self::$annotationMetadata[$name]['default_property'];
if (!$property) {
throw \Doctrine\Common\Annotations\AnnotationException::creationError(\sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, \json_encode($values)));
}
}
$instance->{$property} = $value;
@ -608,6 +671,8 @@ final class ConstantPreservingDocParser
/**
* MethodCall ::= ["(" [Values] ")"]
*
* @return mixed[]
*
* @throws AnnotationException
* @throws ReflectionException
*/
@ -627,6 +692,8 @@ final class ConstantPreservingDocParser
/**
* Values ::= Array | Value {"," Value}* [","]
*
* @return mixed[]
*
* @throws AnnotationException
* @throws ReflectionException
*/
@ -640,23 +707,21 @@ final class ConstantPreservingDocParser
}
$token = $this->lexer->lookahead;
$value = $this->Value();
if (!is_object($value) && !is_array($value)) {
if (!\is_object($value) && !\is_array($value)) {
throw $this->syntaxError('Value', $token);
}
$values[] = $value;
}
foreach ($values as $k => $value) {
if (is_object($value) && $value instanceof \stdClass) {
if (\is_object($value) && $value instanceof \stdClass) {
$values[$value->name] = $value->value;
} elseif (!isset($values['value'])) {
$values['value'] = $value;
} else {
if (!isset($values['value'])) {
$values['value'] = $value;
} else {
if (!is_array($values['value'])) {
$values['value'] = [$values['value']];
}
$values['value'][] = $value;
if (!\is_array($values['value'])) {
$values['value'] = [$values['value']];
}
$values['value'][] = $value;
}
unset($values[$k]);
}
@ -673,16 +738,16 @@ final class ConstantPreservingDocParser
{
$identifier = $this->Identifier();
$originalIdentifier = $identifier;
if (!defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) {
list($className, $const) = explode('::', $identifier);
$pos = strpos($className, '\\');
$alias = false === $pos ? $className : substr($className, 0, $pos);
if (!\defined($identifier) && \strpos($identifier, '::') !== false && $identifier[0] !== '\\') {
[$className, $const] = \explode('::', $identifier);
$pos = \strpos($className, '\\');
$alias = $pos === false ? $className : \substr($className, 0, $pos);
$found = false;
$loweredAlias = strtolower($alias);
$loweredAlias = \strtolower($alias);
switch (true) {
case !empty($this->namespaces):
foreach ($this->namespaces as $ns) {
if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) {
if (\class_exists($ns . '\\' . $className) || \interface_exists($ns . '\\' . $className)) {
$className = $ns . '\\' . $className;
$found = true;
break;
@ -691,12 +756,12 @@ final class ConstantPreservingDocParser
break;
case isset($this->imports[$loweredAlias]):
$found = true;
$className = false !== $pos ? $this->imports[$loweredAlias] . substr($className, $pos) : $this->imports[$loweredAlias];
$className = $pos !== false ? $this->imports[$loweredAlias] . \substr($className, $pos) : $this->imports[$loweredAlias];
break;
default:
if (isset($this->imports['__NAMESPACE__'])) {
$ns = $this->imports['__NAMESPACE__'];
if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) {
if (\class_exists($ns . '\\' . $className) || \interface_exists($ns . '\\' . $className)) {
$className = $ns . '\\' . $className;
$found = true;
}
@ -711,36 +776,36 @@ final class ConstantPreservingDocParser
* Checks if identifier ends with ::class and remove the leading backslash if it exists.
*/
if ($this->identifierEndsWithClassConstant($identifier) && !$this->identifierStartsWithBackslash($identifier)) {
$resolvedValue = substr($identifier, 0, $this->getClassConstantPositionInIdentifier($identifier));
$resolvedValue = \substr($identifier, 0, $this->getClassConstantPositionInIdentifier($identifier));
\Rector\DoctrineAnnotationGenerated\DataCollector\ResolvedConstantStaticCollector::collect($originalIdentifier, $resolvedValue);
return $resolvedValue;
}
if ($this->identifierEndsWithClassConstant($identifier) && $this->identifierStartsWithBackslash($identifier)) {
$resolvedValue = substr($identifier, 1, $this->getClassConstantPositionInIdentifier($identifier) - 1);
$resolvedValue = \substr($identifier, 1, $this->getClassConstantPositionInIdentifier($identifier) - 1);
\Rector\DoctrineAnnotationGenerated\DataCollector\ResolvedConstantStaticCollector::collect($originalIdentifier, $resolvedValue);
return $resolvedValue;
}
if (!defined($identifier)) {
if (!\defined($identifier)) {
throw \Doctrine\Common\Annotations\AnnotationException::semanticalErrorConstants($identifier, $this->context);
}
$resolvedValue = constant($identifier);
$resolvedValue = \constant($identifier);
\Rector\DoctrineAnnotationGenerated\DataCollector\ResolvedConstantStaticCollector::collect($originalIdentifier, $resolvedValue);
return $resolvedValue;
}
private function identifierStartsWithBackslash(string $identifier): bool
{
return '\\' === $identifier[0];
return $identifier[0] === '\\';
}
private function identifierEndsWithClassConstant(string $identifier): bool
{
return $this->getClassConstantPositionInIdentifier($identifier) === strlen($identifier) - strlen('::class');
return $this->getClassConstantPositionInIdentifier($identifier) === \strlen($identifier) - \strlen('::class');
}
/**
* @return int|false
*/
private function getClassConstantPositionInIdentifier(string $identifier)
{
return stripos($identifier, '::class');
return \stripos($identifier, '::class');
}
/**
* Identifier ::= string
@ -755,7 +820,7 @@ final class ConstantPreservingDocParser
}
$this->lexer->moveNext();
$className = $this->lexer->token['value'];
while (null !== $this->lexer->lookahead && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value']) && $this->lexer->isNextToken(\Doctrine\Common\Annotations\DocLexer::T_NAMESPACE_SEPARATOR)) {
while ($this->lexer->lookahead !== null && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + \strlen($this->lexer->token['value']) && $this->lexer->isNextToken(\Doctrine\Common\Annotations\DocLexer::T_NAMESPACE_SEPARATOR)) {
$this->match(\Doctrine\Common\Annotations\DocLexer::T_NAMESPACE_SEPARATOR);
$this->matchAny(self::$classIdentifiers);
$className .= '\\' . $this->lexer->token['value'];
@ -766,13 +831,14 @@ final class ConstantPreservingDocParser
* Value ::= PlainValue | FieldAssignment
*
* @return mixed
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function Value()
{
$peek = $this->lexer->glimpse();
if (\Doctrine\Common\Annotations\DocLexer::T_EQUALS === $peek['type']) {
if ($peek['type'] === \Doctrine\Common\Annotations\DocLexer::T_EQUALS) {
return $this->FieldAssignment();
}
return $this->PlainValue();
@ -781,6 +847,7 @@ final class ConstantPreservingDocParser
* PlainValue ::= integer | string | float | boolean | Array | Annotation
*
* @return mixed
*
* @throws AnnotationException
* @throws ReflectionException
*/
@ -821,6 +888,7 @@ final class ConstantPreservingDocParser
/**
* FieldAssignment ::= FieldName "=" PlainValue
* FieldName ::= identifier
*
* @throws AnnotationException
* @throws ReflectionException
*/
@ -836,6 +904,9 @@ final class ConstantPreservingDocParser
}
/**
* Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}"
*
* @return mixed[]
*
* @throws AnnotationException
* @throws ReflectionException
*/
@ -859,7 +930,7 @@ final class ConstantPreservingDocParser
}
$this->match(\Doctrine\Common\Annotations\DocLexer::T_CLOSE_CURLY_BRACES);
foreach ($values as $value) {
list($key, $val) = $value;
[$key, $val] = $value;
if ($key !== null) {
$array[$key] = $val;
} else {
@ -875,11 +946,13 @@ final class ConstantPreservingDocParser
*
* @throws AnnotationException
* @throws ReflectionException
*
* @phpstan-return array{mixed, mixed}
*/
private function ArrayEntry(): array
{
$peek = $this->lexer->glimpse();
if (\Doctrine\Common\Annotations\DocLexer::T_EQUALS === $peek['type'] || \Doctrine\Common\Annotations\DocLexer::T_COLON === $peek['type']) {
if ($peek['type'] === \Doctrine\Common\Annotations\DocLexer::T_EQUALS || $peek['type'] === \Doctrine\Common\Annotations\DocLexer::T_COLON) {
if ($this->lexer->isNextToken(\Doctrine\Common\Annotations\DocLexer::T_IDENTIFIER)) {
$key = $this->Constant();
} else {
@ -899,9 +972,9 @@ final class ConstantPreservingDocParser
if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) {
return true;
}
foreach (array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) {
$ignoredAnnotationNamespace = rtrim($ignoredAnnotationNamespace, '\\') . '\\';
if (0 === stripos(rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace)) {
foreach (\array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) {
$ignoredAnnotationNamespace = \rtrim($ignoredAnnotationNamespace, '\\') . '\\';
if (\stripos(\rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace) === 0) {
return true;
}
}

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\DynamicTypeAnalysis\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\NodeNameResolver\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Contract']);

View File

@ -20,7 +20,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\NodeTypeResolver\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Contract', __DIR__ . '/../src/PHPStan/TypeExtension']);

View File

@ -209,4 +209,9 @@ final class AttributeKey
* @var string
*/
public const IS_FIRST_LEVEL_STATEMENT = 'is_first_level_statement';
/**
* @var string
*/
public const IS_FRESH_NODE = 'is_fresh_node';
}

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\PHPStanStaticTypeMapper\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/ValueObject']);

View File

@ -7,11 +7,11 @@
"symfony/dependency-injection": "^4.4.8|^5.1",
"symfony/config": "^4.4.8|^5.1",
"symfony/http-kernel": "^4.4.8|^5.1",
"symplify/package-builder": "^8.3.40"
"symplify/package-builder": "^8.3.45"
},
"require-dev": {
"phpunit/phpunit": "^8.5|^9.2",
"symplify/easy-testing": "^8.3.40"
"symplify/easy-testing": "^8.3.45"
},
"autoload": {
"psr-4": {

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\StaticTypeMapper\\', __DIR__ . '/../src');
};

View File

@ -6,7 +6,7 @@
"php": "^7.2.4|^8.0",
"symfony/dependency-injection": "^4.4.8|^5.1",
"symfony/http-kernel": "^4.4.8|^5.1",
"symplify/package-builder": "^8.3.40"
"symplify/package-builder": "^8.3.45"
},
"require-dev": {
"phpunit/phpunit": "^8.5|^9.2"

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\VendorLocker\\', __DIR__ . '/../src');
};

View File

@ -11,6 +11,7 @@ use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
use Rector\Set\ValueObject\SetList;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Rector\SymfonyPhpConfig\Rector\MethodCall\AutoInPhpSymfonyConfigRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -23,6 +24,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
]),
]]);
$services->set(AutoInPhpSymfonyConfigRector::class);
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\Architecture\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\Autodiscovery\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\CakePHP\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\CodeQuality\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class IfConditionMethodCallWithArg
{
@ -25,7 +25,7 @@ class IfConditionMethodCallWithArg
-----
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class IfConditionMethodCallWithArg
{

View File

@ -1,19 +1,18 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
use Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Source\SomeClassWithConstants;
class IfConditionMethodCallWithArgClassConstant
{
private const ARG1 = 1;
private const ARG2 = 2;
public function run($arg)
{
$obj = new self();
if ($obj->condition(IfConditionMethodCallWithArg::ARG1) === 1) {
if ($obj->condition(SomeClassWithConstants::ARG1) === 1) {
}
if ($obj->condition(IfConditionMethodCallWithArg::ARG2) === 2) {
if ($obj->condition(SomeClassWithConstants::ARG2) === 2) {
}
}
@ -28,21 +27,20 @@ class IfConditionMethodCallWithArgClassConstant
-----
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
use Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Source\SomeClassWithConstants;
class IfConditionMethodCallWithArgClassConstant
{
private const ARG1 = 1;
private const ARG2 = 2;
public function run($arg)
{
$obj = new self();
$objArg1 = $obj->condition(IfConditionMethodCallWithArg::ARG1);
$objArg1 = $obj->condition(SomeClassWithConstants::ARG1);
if ($objArg1 === 1) {
}
$objArg2 = $obj->condition(IfConditionMethodCallWithArg::ARG2);
$objArg2 = $obj->condition(SomeClassWithConstants::ARG2);
if ($objArg2 === 2) {
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
use Rector\Core\Configuration\Option;
use Symfony\Component\Console\Command\Command;
@ -26,7 +26,7 @@ abstract class AbstractCommand extends Command
-----
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
use Rector\Core\Configuration\Option;
use Symfony\Component\Console\Command\Command;

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class SkipFromThis
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class IfConditionMethodCallWithArgWithBoolReturn
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class IfConditionMethodCallWithArgWithMultipleCall
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class IfConditionWithMethodCallInside
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
class SkipNoArg
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
$data = 1;
if ($data === 1) {

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Source;
final class SomeClassWithConstants
{
public const ARG1 = 'arg_1';
public const ARG2 = 'arg_2';
}

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\CodingStyle\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\DeadCode\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->autowire()
->public();
->public()
->autoconfigure();
$services->load('Rector\Decouple\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Defluent\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -99,6 +99,21 @@ final class FluentChainMethodCallNodeAnalyzer
return $nextNode === null;
}
/**
* @return string[]|null[]
*/
public function collectMethodCallNamesInChain(MethodCall $desiredMethodCall): array
{
$methodCalls = $this->collectAllMethodCallsInChain($desiredMethodCall);
$methodNames = [];
foreach ($methodCalls as $methodCall) {
$methodNames[] = $this->nodeNameResolver->getName($methodCall->name);
}
return $methodNames;
}
/**
* @return MethodCall[]
*/

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Generic\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Contract', __DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Laravel\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\MagicDisclosure\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\MockeryToProphecy\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\MockistaToMockery\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\NetteCodeQuality\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\NetteKdyby\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\NetteTesterToPHPUnit\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -12,7 +12,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\NetteToSymfony\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Nette\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Contract', __DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Order\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\PhpSpecToPHPUnit\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Php55\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Php70\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/Exception', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Php71\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Php80\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\PHPUnit\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Polyfill\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\RemovingStatic\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Renaming\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Restoration\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Sensio\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\SOLID\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -12,7 +12,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\SymfonyPhpConfig\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPhpConfig\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Defluent\NodeAnalyzer\FluentChainMethodCallNodeAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\SymfonyPhpConfig\NodeAnalyzer\SymfonyPhpConfigClosureAnalyzer;
/**
* @see phpstan rule https://github.com/symplify/coding-standard/blob/master/docs/phpstan_rules.md#check-required-autowire-autoconfigure-and-public-are-used-in-config-service-rule
* @see \Rector\SymfonyPhpConfig\Tests\Rector\MethodCall\AutoInPhpSymfonyConfigRector\AutoInPhpSymfonyConfigRectorTest
*/
final class AutoInPhpSymfonyConfigRector extends AbstractRector
{
/**
* @var string[]
*/
private const REQUIRED_METHOD_NAMES = ['public', 'autowire', 'autoconfigure'];
/**
* @var SymfonyPhpConfigClosureAnalyzer
*/
private $symfonyPhpConfigClosureAnalyzer;
/**
* @var FluentChainMethodCallNodeAnalyzer
*/
private $fluentChainMethodCallNodeAnalyzer;
public function __construct(
SymfonyPhpConfigClosureAnalyzer $symfonyPhpConfigClosureAnalyzer,
FluentChainMethodCallNodeAnalyzer $fluentChainMethodCallNodeAnalyzer
) {
$this->symfonyPhpConfigClosureAnalyzer = $symfonyPhpConfigClosureAnalyzer;
$this->fluentChainMethodCallNodeAnalyzer = $fluentChainMethodCallNodeAnalyzer;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Make sure there is public(), autowire(), autoconfigure() calls on defaults() in Symfony configs',
[
new CodeSample(
<<<'CODE_SAMPLE'
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->autowire();
};
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->autowire()
->public()
->autoconfigure();
};
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [MethodCall::class];
}
/**
* @param MethodCall $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkipMethodCall($node)) {
return null;
}
$missingMethodNames = $this->resolveMissingMethodNames($node);
if ($missingMethodNames === []) {
return null;
}
$node->setAttribute(AttributeKey::IS_FRESH_NODE, true);
$methodCall = clone $node;
foreach ($missingMethodNames as $missingMethodName) {
$methodCall = new MethodCall($methodCall, $missingMethodName);
}
return $methodCall;
}
private function shouldSkipMethodCall(MethodCall $methodCall): bool
{
if ($methodCall->getAttribute(AttributeKey::IS_FRESH_NODE)) {
return true;
}
$closure = $methodCall->getAttribute(AttributeKey::CLOSURE_NODE);
if ($closure === null) {
return true;
}
if (! $this->symfonyPhpConfigClosureAnalyzer->isPhpConfigClosure($closure)) {
return true;
}
if (! $this->fluentChainMethodCallNodeAnalyzer->isLastChainMethodCall($methodCall)) {
return true;
}
$rootMethodCall = $this->fluentChainMethodCallNodeAnalyzer->resolveRootMethodCall($methodCall);
if ($rootMethodCall === null) {
return true;
}
return ! $this->isName($rootMethodCall->name, 'defaults');
}
/**
* @return string[]
*/
private function resolveMissingMethodNames(MethodCall $methodCall): array
{
$methodCallNames = $this->fluentChainMethodCallNodeAnalyzer->collectMethodCallNamesInChain($methodCall);
return array_diff(self::REQUIRED_METHOD_NAMES, $methodCallNames);
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPhpConfig\Tests\Rector\MethodCall\AutoInPhpSymfonyConfigRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\SymfonyPhpConfig\Rector\MethodCall\AutoInPhpSymfonyConfigRector;
use Symplify\SmartFileSystem\SmartFileInfo;
final class AutoInPhpSymfonyConfigRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return AutoInPhpSymfonyConfigRector::class;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\SymfonyPhpConfig\Tests\Rector\MethodCall\AutoInPhpSymfonyConfigRector\Fixture;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->autowire();
};
?>
-----
<?php
namespace Rector\SymfonyPhpConfig\Tests\Rector\MethodCall\AutoInPhpSymfonyConfigRector\Fixture;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->autowire()->public()->autoconfigure();
};
?>

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\SymfonyPhpConfig\Tests\Rector\MethodCall\AutoInPhpSymfonyConfigRector\Fixture;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->autowire()
->public()
->autoconfigure();
};

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\SymfonyPHPUnit\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);

View File

@ -13,7 +13,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Symfony\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/Exception', __DIR__ . '/../src/ValueObject']);

View File

@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->defaults()
->public()
->autowire();
->autowire()
->autoconfigure();
$services->load('Rector\Transform\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);

View File

@ -12,7 +12,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->autowire();
->autowire()
->public()
->autoconfigure();
$services->set(SymfonyVersionFeatureGuard::class);
$services->alias(SymfonyVersionFeatureGuardInterface::class, SymfonyVersionFeatureGuard::class);