mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-28 23:10:51 +00:00
[DoctrineAnnotaitonGenerated] Update to doctrine/annotations 1.11 + fix static (#4479)
This commit is contained in:
parent
5bb03f2fcb
commit
0f2e6d407e
|
@ -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(
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -14,7 +14,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
$services->defaults()
|
||||
->autowire()
|
||||
->public();
|
||||
->public()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\BetterPhpDocParser\\', __DIR__ . '/../src')
|
||||
->exclude([
|
||||
|
|
|
@ -26,7 +26,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
$services->defaults()
|
||||
->autowire()
|
||||
->public();
|
||||
->public()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\Caching\\', __DIR__ . '/../src');
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
$services->defaults()
|
||||
->public()
|
||||
->autowire();
|
||||
->autowire()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\ConsoleDiffer\\', __DIR__ . '/../src');
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
$services->defaults()
|
||||
->public()
|
||||
->autowire();
|
||||
->autowire()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\StaticTypeMapper\\', __DIR__ . '/../src');
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -9,7 +9,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
$services->defaults()
|
||||
->public()
|
||||
->autowire();
|
||||
->autowire()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\VendorLocker\\', __DIR__ . '/../src');
|
||||
};
|
||||
|
|
|
@ -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, [
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
|
||||
class SkipFromThis
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
|
||||
class IfConditionMethodCallWithArgWithBoolReturn
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
|
||||
class IfConditionMethodCallWithArgWithMultipleCall
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
|
||||
class IfConditionWithMethodCallInside
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Php\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
namespace Rector\CodeQuality\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
|
||||
class SkipNoArg
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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';
|
||||
}
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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[]
|
||||
*/
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
||||
?>
|
|
@ -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();
|
||||
};
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user