mirror of
https://github.com/rectorphp/rector.git
synced 2025-04-21 16:02:23 +02:00
[Symfony] refactor to ServiceMap
This commit is contained in:
parent
7292171e69
commit
c1c0e37690
11
README.md
11
README.md
@ -221,6 +221,17 @@ vendor/bin/rector process src --match-git-diff
|
||||
|
||||
This option is useful in CI with pull-requests that only change few files.
|
||||
|
||||
### Symfony Container
|
||||
|
||||
To work with some Symfony rules, you now need to link your container XML file
|
||||
|
||||
```yaml
|
||||
# rector.yaml
|
||||
parameters:
|
||||
# path to load services from
|
||||
symfony_container_xml_path: 'var/cache/dev/AppKernelDevDebugContainer.xml'
|
||||
```
|
||||
|
||||
## 3 Steps to Create Your Own Rector
|
||||
|
||||
First, make sure it's not covered by [any existing Rectors](/docs/AllRectorsOverview.md).
|
||||
|
@ -43,7 +43,6 @@ services:
|
||||
|
||||
# alises
|
||||
Symfony\Contracts\EventDispatcher\EventDispatcherInterface: '@Rector\EventDispatcher\AutowiredEventDispatcher'
|
||||
Rector\Bridge\Contract\AnalyzedApplicationContainerInterface: '@Rector\Symfony\Bridge\DefaultAnalyzedSymfonyApplicationContainer'
|
||||
|
||||
OndraM\CiDetector\CiDetector: ~
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
parameters:
|
||||
# explicit environment of analyzed application
|
||||
kernel_environment: ''
|
||||
# explicit kernel class of analyzed application
|
||||
kernel_class: ''
|
||||
# path to load services from
|
||||
symfony_container_xml_path: ''
|
||||
|
||||
services:
|
||||
_defaults:
|
||||
@ -13,4 +11,6 @@ services:
|
||||
resource: '../src'
|
||||
exclude:
|
||||
- '../src/Rector/**/*Rector.php'
|
||||
- '../src/Exception/*'
|
||||
- '../src/ValueObject/*'
|
||||
- '../src/PhpDocParser/Ast/PhpDoc/*'
|
||||
|
@ -1,164 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Bridge;
|
||||
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\Symfony\Bridge\DependencyInjection\SymfonyContainerFactory;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
use Throwable;
|
||||
|
||||
final class DefaultAnalyzedSymfonyApplicationContainer implements AnalyzedApplicationContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var SymfonyKernelParameterGuard
|
||||
*/
|
||||
private $symfonyKernelParameterGuard;
|
||||
|
||||
/**
|
||||
* @var SymfonyContainerFactory
|
||||
*/
|
||||
private $symfonyContainerFactory;
|
||||
|
||||
/**
|
||||
* @var ParameterProvider
|
||||
*/
|
||||
private $parameterProvider;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface[]
|
||||
*/
|
||||
private $containerByKernelClass = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $commonNamesToTypes = [
|
||||
'doctrine' => 'Symfony\Bridge\Doctrine\RegistryInterface',
|
||||
'doctrine.orm.entity_manager' => 'Doctrine\ORM\EntityManagerInterface',
|
||||
'doctrine.orm.default_entity_manager' => 'Doctrine\ORM\EntityManagerInterface',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
SymfonyKernelParameterGuard $symfonyKernelParameterGuard,
|
||||
SymfonyContainerFactory $symfonyContainerFactory,
|
||||
ParameterProvider $parameterProvider
|
||||
) {
|
||||
$this->symfonyKernelParameterGuard = $symfonyKernelParameterGuard;
|
||||
$this->symfonyContainerFactory = $symfonyContainerFactory;
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
}
|
||||
|
||||
public function getTypeForName(string $name): Type
|
||||
{
|
||||
if (isset($this->commonNamesToTypes[$name])) {
|
||||
return new ObjectType($this->commonNamesToTypes[$name]);
|
||||
}
|
||||
|
||||
// get known Symfony types
|
||||
$container = $this->getContainer($name);
|
||||
|
||||
if (! $container->has($name)) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
try {
|
||||
$service = $container->get($name);
|
||||
} catch (Throwable $throwable) {
|
||||
throw new ShouldNotHappenException(sprintf(
|
||||
'Service type for "%s" name was not found in container of your Symfony application.',
|
||||
$name
|
||||
), $throwable->getCode(), $throwable);
|
||||
}
|
||||
|
||||
if ($service === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$serviceClass = get_class($service);
|
||||
if ($container->has($serviceClass)) {
|
||||
return new ObjectType($serviceClass);
|
||||
}
|
||||
|
||||
// the type was not found in container → use it's interface or parent
|
||||
// mimics: \Symfony\Component\DependencyInjection\Compiler\AutowirePass::getAliasesSuggestionForType()
|
||||
foreach (class_implements($serviceClass) + class_parents($serviceClass) as $parent) {
|
||||
if ($container->has($parent) && ! $container->findDefinition($parent)->isAbstract()) {
|
||||
return new ObjectType($parent);
|
||||
}
|
||||
}
|
||||
|
||||
// make an assumption
|
||||
return new ObjectType(get_class($service));
|
||||
}
|
||||
|
||||
public function hasService(string $name): bool
|
||||
{
|
||||
$container = $this->getContainer($name);
|
||||
|
||||
return $container->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return object
|
||||
*/
|
||||
public function getService(string $name)
|
||||
{
|
||||
$container = $this->getContainer($name);
|
||||
|
||||
return $container->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContainerBuilder
|
||||
*/
|
||||
private function getContainer(string $requestServiceName): Container
|
||||
{
|
||||
$kernelClass = $this->resolveKernelClass();
|
||||
|
||||
if (isset($this->containerByKernelClass[$kernelClass])) {
|
||||
return $this->containerByKernelClass[$kernelClass];
|
||||
}
|
||||
|
||||
$this->symfonyKernelParameterGuard->ensureKernelClassIsValid($kernelClass, $requestServiceName);
|
||||
|
||||
/** @var string $kernelClass */
|
||||
$container = $this->symfonyContainerFactory->createFromKernelClass($kernelClass);
|
||||
|
||||
$this->containerByKernelClass[$kernelClass] = $container;
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
private function resolveKernelClass(): ?string
|
||||
{
|
||||
$kernelClassParameter = $this->parameterProvider->provideParameter(Option::KERNEL_CLASS_PARAMETER);
|
||||
if ($kernelClassParameter) {
|
||||
return $kernelClassParameter;
|
||||
}
|
||||
|
||||
return $this->getDefaultKernelClass();
|
||||
}
|
||||
|
||||
private function getDefaultKernelClass(): ?string
|
||||
{
|
||||
$possibleKernelClasses = ['App\Kernel', 'Kernel', 'AppKernel'];
|
||||
|
||||
foreach ($possibleKernelClasses as $possibleKernelClass) {
|
||||
if (class_exists($possibleKernelClass)) {
|
||||
return $possibleKernelClass;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Bridge\DependencyInjection;
|
||||
|
||||
use Nette\Utils\Random;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
|
||||
use Throwable;
|
||||
|
||||
final class SymfonyContainerFactory
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const FALLBACK_ENVIRONMENT = 'dev';
|
||||
|
||||
/**
|
||||
* @var Container[]
|
||||
*/
|
||||
private $containersByKernelClass = [];
|
||||
|
||||
/**
|
||||
* @var ParameterProvider
|
||||
*/
|
||||
private $parameterProvider;
|
||||
|
||||
public function __construct(ParameterProvider $parameterProvider)
|
||||
{
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
}
|
||||
|
||||
public function createFromKernelClass(string $kernelClass): Container
|
||||
{
|
||||
if (isset($this->containersByKernelClass[$kernelClass])) {
|
||||
return $this->containersByKernelClass[$kernelClass];
|
||||
}
|
||||
|
||||
$environment = $this->resolveEnvironment();
|
||||
|
||||
try {
|
||||
$debug = (bool) ($_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? true);
|
||||
|
||||
$container = $this->createContainerFromKernelClass($kernelClass, $environment, $debug);
|
||||
} catch (Throwable $throwable) {
|
||||
throw new ShouldNotHappenException(sprintf(
|
||||
'Kernel "%s" could not be instantiated for: %s.%sCurrent environment is "%s". Try changing it in "parameters > kernel_environment" in rector.yaml',
|
||||
$kernelClass,
|
||||
$throwable->getMessage(),
|
||||
PHP_EOL . PHP_EOL,
|
||||
$environment
|
||||
), $throwable->getCode(), $throwable);
|
||||
}
|
||||
|
||||
$this->containersByKernelClass[$kernelClass] = $container;
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
private function resolveEnvironment(): string
|
||||
{
|
||||
/** @var string|null $kernelEnvironment */
|
||||
$kernelEnvironment = $this->parameterProvider->provideParameter(Option::KERNEL_ENVIRONMENT_PARAMETER);
|
||||
if ($kernelEnvironment) {
|
||||
return $kernelEnvironment;
|
||||
}
|
||||
|
||||
return $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? self::FALLBACK_ENVIRONMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimics https://github.com/symfony/symfony/blob/f834c9262b411aa5793fcea23694e3ad3b5acbb4/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php#L200-L203
|
||||
*/
|
||||
private function createContainerFromKernelClass(string $kernelClass, string $environment, bool $debug): Container
|
||||
{
|
||||
/** @var Kernel $kernel */
|
||||
$kernel = new $kernelClass($environment, $debug);
|
||||
// preloads all the extensions and $containerBuilder->compile()
|
||||
$kernel->boot();
|
||||
|
||||
/** @var ContainerBuilder $containerBuilder */
|
||||
$containerBuilder = (new PrivatesCaller())->callPrivateMethod($kernel, 'buildContainer');
|
||||
$containerBuilder->getCompilerPassConfig()->setRemovingPasses([]);
|
||||
|
||||
// anonymous class on intention, since this depends on Symfony\DependencyInjection in rector-prefixed
|
||||
$containerBuilder->getCompilerPassConfig()->addPass(new class() implements CompilerPassInterface {
|
||||
public function process(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
foreach ($containerBuilder->getDefinitions() as $definition) {
|
||||
$definition->setPublic(true);
|
||||
}
|
||||
foreach ($containerBuilder->getAliases() as $definition) {
|
||||
$definition->setPublic(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$containerBuilder->compile(true);
|
||||
|
||||
// solves "You have requested a synthetic service ("kernel"). The DIC does not know how to construct this service"
|
||||
$containerBuilder->set('kernel', $kernel);
|
||||
|
||||
// solves "You have requested a non-existent parameter "container.build_id"
|
||||
if ($containerBuilder->getParameterBag() instanceof FrozenParameterBag) {
|
||||
$unfrozenParameterBag = new ParameterBag($containerBuilder->getParameterBag()->all());
|
||||
|
||||
$privatesAccessor = new PrivatesAccessor();
|
||||
$privatesAccessor->setPrivateProperty($containerBuilder, 'parameterBag', $unfrozenParameterBag);
|
||||
}
|
||||
|
||||
if (! $containerBuilder->hasParameter('container.build_id')) {
|
||||
$containerBuilder->setParameter('container.build_id', Random::generate(10));
|
||||
}
|
||||
|
||||
return $containerBuilder;
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Bridge;
|
||||
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Exception\Configuration\InvalidConfigurationException;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
final class SymfonyKernelParameterGuard
|
||||
{
|
||||
public function ensureKernelClassIsValid(?string $kernelClass, string $requestServiceName): void
|
||||
{
|
||||
// ensure value is not null nor empty
|
||||
if ($kernelClass === null || $kernelClass === '') {
|
||||
throw new InvalidConfigurationException(sprintf(
|
||||
'Make sure "parameters > %s" is set in rector.yaml.%sIt is needed to resolve "%s" service name to type',
|
||||
Option::KERNEL_CLASS_PARAMETER,
|
||||
PHP_EOL,
|
||||
$requestServiceName
|
||||
));
|
||||
}
|
||||
|
||||
$this->ensureKernelClassExists($kernelClass);
|
||||
$this->ensureIsKernelInstance($kernelClass);
|
||||
}
|
||||
|
||||
private function ensureKernelClassExists(string $kernelClass): void
|
||||
{
|
||||
if (class_exists($kernelClass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidConfigurationException(sprintf(
|
||||
'Kernel class "%s" provided in "parameters > %s" is not autoloadable. Make sure composer.json of your application is valid and rector is loading "vendor/autoload.php" of your application.',
|
||||
$kernelClass,
|
||||
Option::KERNEL_CLASS_PARAMETER
|
||||
));
|
||||
}
|
||||
|
||||
private function ensureIsKernelInstance(string $kernelClass): void
|
||||
{
|
||||
if (is_a($kernelClass, Kernel::class, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidConfigurationException(sprintf(
|
||||
'Kernel class "%s" provided in "parameters > %s" is not instance of "%s". Make sure it is.',
|
||||
$kernelClass,
|
||||
'kernel_class',
|
||||
Kernel::class
|
||||
));
|
||||
}
|
||||
}
|
10
packages/Symfony/src/Contract/Tag/TagInterface.php
Normal file
10
packages/Symfony/src/Contract/Tag/TagInterface.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Contract\Tag;
|
||||
|
||||
interface TagInterface
|
||||
{
|
||||
public function getName(): string;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class XmlContainerNotExistsException extends Exception
|
||||
{
|
||||
}
|
@ -13,11 +13,11 @@ use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\Naming\PropertyNaming;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\Symfony\ServiceMapProvider;
|
||||
|
||||
abstract class AbstractToConstructorInjectionRector extends AbstractRector
|
||||
{
|
||||
@ -27,19 +27,19 @@ abstract class AbstractToConstructorInjectionRector extends AbstractRector
|
||||
protected $propertyNaming;
|
||||
|
||||
/**
|
||||
* @var AnalyzedApplicationContainerInterface
|
||||
* @var ServiceMapProvider
|
||||
*/
|
||||
protected $analyzedApplicationContainer;
|
||||
private $applicationServiceMapProvider;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setAbstractToConstructorInjectionRectorDependencies(
|
||||
public function autowireAbstractToConstructorInjectionRectorDependencies(
|
||||
PropertyNaming $propertyNaming,
|
||||
AnalyzedApplicationContainerInterface $analyzedApplicationContainer
|
||||
ServiceMapProvider $applicationServiceMapProvider
|
||||
): void {
|
||||
$this->propertyNaming = $propertyNaming;
|
||||
$this->analyzedApplicationContainer = $analyzedApplicationContainer;
|
||||
$this->applicationServiceMapProvider = $applicationServiceMapProvider;
|
||||
}
|
||||
|
||||
protected function processMethodCallNode(MethodCall $methodCall): ?Node
|
||||
@ -71,10 +71,10 @@ abstract class AbstractToConstructorInjectionRector extends AbstractRector
|
||||
|
||||
$argument = $methodCallNode->args[0]->value;
|
||||
|
||||
if ($argument instanceof String_) {
|
||||
$serviceName = $argument->value;
|
||||
$serviceMap = $this->applicationServiceMapProvider->provide();
|
||||
|
||||
return $this->analyzedApplicationContainer->getTypeForName($serviceName);
|
||||
if ($argument instanceof String_) {
|
||||
return $serviceMap->getServiceType($argument->value);
|
||||
}
|
||||
|
||||
if (! $argument instanceof ClassConstFetch) {
|
||||
|
231
packages/Symfony/src/ServiceMapProvider.php
Normal file
231
packages/Symfony/src/ServiceMapProvider.php
Normal file
@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony;
|
||||
|
||||
use Nette\Utils\FileSystem;
|
||||
use Nette\Utils\Json;
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Symfony\Contract\Tag\TagInterface;
|
||||
use Rector\Symfony\Exception\XmlContainerNotExistsException;
|
||||
use Rector\Symfony\ValueObject\ServiceDefinition;
|
||||
use Rector\Symfony\ValueObject\ServiceMap\ServiceMap;
|
||||
use Rector\Symfony\ValueObject\Tag;
|
||||
use Rector\Symfony\ValueObject\Tag\EventListenerTag;
|
||||
use SimpleXMLElement;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
|
||||
/**
|
||||
* Inspired by https://github.com/phpstan/phpstan-symfony/tree/master/src/Symfony
|
||||
*/
|
||||
final class ServiceMapProvider
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const SYMFONY_CONTAINER_XML_PATH_PARAMETER = 'symfony_container_xml_path';
|
||||
|
||||
/**
|
||||
* @var ParameterProvider
|
||||
*/
|
||||
private $parameterProvider;
|
||||
|
||||
public function __construct(ParameterProvider $parameterProvider)
|
||||
{
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
}
|
||||
|
||||
public function provide(): ServiceMap
|
||||
{
|
||||
$symfonyContainerXmlPath = $this->getSymfonyContainerXmlPath();
|
||||
if ($symfonyContainerXmlPath === '') {
|
||||
return new ServiceMap([]);
|
||||
}
|
||||
|
||||
$fileContents = FileSystem::read($symfonyContainerXmlPath);
|
||||
|
||||
return $this->createServiceMapFromXml($fileContents);
|
||||
}
|
||||
|
||||
private function createServiceMapFromXml(string $fileContents): ServiceMap
|
||||
{
|
||||
$xml = @simplexml_load_string($fileContents);
|
||||
|
||||
if (! $xml) {
|
||||
throw new XmlContainerNotExistsException(sprintf(
|
||||
'Container %s cannot be parsed', $this->getSymfonyContainerXmlPath()
|
||||
));
|
||||
}
|
||||
|
||||
/** @var ServiceDefinition[] $services */
|
||||
$services = [];
|
||||
|
||||
/** @var ServiceDefinition[] $aliases */
|
||||
$aliases = [];
|
||||
|
||||
foreach ($xml->services->service as $def) {
|
||||
/** @var SimpleXMLElement $attrs */
|
||||
$attrs = $def->attributes();
|
||||
if (! isset($attrs->id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$def = $this->convertXmlToArray($def);
|
||||
$tags = $this->createTagFromXmlElement($def);
|
||||
|
||||
$service = $this->createServiceFromXml($attrs, $tags);
|
||||
if ($service->getAlias() !== null) {
|
||||
$aliases[] = $service;
|
||||
} else {
|
||||
$services[$service->getId()] = $service;
|
||||
}
|
||||
}
|
||||
|
||||
$services = $this->createAliasServiceDefinitions($aliases, $services);
|
||||
|
||||
return new ServiceMap($services);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $tags
|
||||
*/
|
||||
private function createServiceFromXml(SimpleXMLElement $attrs, array $tags): ServiceDefinition
|
||||
{
|
||||
$tags = $this->createTagsFromXml($tags);
|
||||
|
||||
return new ServiceDefinition(
|
||||
strpos((string) $attrs->id, '.') === 0 ? Strings::substring((string) $attrs->id, 1) : (string) $attrs->id,
|
||||
isset($attrs->class) ? (string) $attrs->class : null,
|
||||
! isset($attrs->public) || (string) $attrs->public !== 'false',
|
||||
isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
|
||||
isset($attrs->alias) ? (string) $attrs->alias : null,
|
||||
$tags
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $tags
|
||||
* @return TagInterface[]
|
||||
*/
|
||||
private function createTagsFromXml(array $tags): array
|
||||
{
|
||||
$tagValueObjects = [];
|
||||
foreach ($tags as $key => $tag) {
|
||||
$data = $tag;
|
||||
$name = $data['name'] ?? '';
|
||||
|
||||
if ($name === 'kernel.event_listener') {
|
||||
$tagValueObjects[$key] = new EventListenerTag(
|
||||
$data['event'] ?? '',
|
||||
$data['method'] ?? '',
|
||||
(int) ($data['priority'] ?? 0)
|
||||
);
|
||||
} else {
|
||||
unset($data['name']);
|
||||
$tagValueObjects[$key] = new Tag($name, $data);
|
||||
}
|
||||
}
|
||||
|
||||
return $tagValueObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function convertXmlToArray(SimpleXMLElement $simpleXMLElement): array
|
||||
{
|
||||
$data = Json::decode(Json::encode((array) $simpleXMLElement), Json::FORCE_ARRAY);
|
||||
|
||||
$data = $this->unWrapAttributes($data);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$data = $this->convertedNestedArrayOrXml($value, $data, $key);
|
||||
} elseif ($value instanceof SimpleXMLElement) {
|
||||
$data[$key] = $this->convertXmlToArray($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function unWrapAttributes(array $data): array
|
||||
{
|
||||
if (isset($data['@attributes'])) {
|
||||
foreach ($data['@attributes'] as $key => $value) {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
|
||||
unset($data['@attributes']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function createTagFromXmlElement($def): array
|
||||
{
|
||||
if (! isset($def['tag'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tags = [];
|
||||
if (is_array($def['tag'])) {
|
||||
foreach ($def['tag'] as $tag) {
|
||||
$tags[] = $tag;
|
||||
}
|
||||
} else {
|
||||
$tags[] = $def['tag'];
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
private function convertedNestedArrayOrXml(array $value, array $data, $key): array
|
||||
{
|
||||
foreach ($value as $subKey => $subValue) {
|
||||
if ($subValue instanceof SimpleXMLElement) {
|
||||
$data[$key][$subKey] = $this->convertXmlToArray($subValue);
|
||||
} elseif (is_array($subValue)) {
|
||||
$data[$key][$subKey] = $this->unWrapAttributes($subValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServiceDefinition[] $aliases
|
||||
* @param ServiceDefinition[] $services
|
||||
* @return ServiceDefinition[]
|
||||
*/
|
||||
private function createAliasServiceDefinitions(array $aliases, array $services): array
|
||||
{
|
||||
foreach ($aliases as $service) {
|
||||
$alias = $service->getAlias();
|
||||
if ($alias === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! isset($services[$alias])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = $service->getId();
|
||||
$services[$id] = new ServiceDefinition(
|
||||
$id,
|
||||
$services[$alias]->getClass(),
|
||||
$service->isPublic(),
|
||||
$service->isSynthetic(),
|
||||
$alias,
|
||||
[]
|
||||
);
|
||||
}
|
||||
return $services;
|
||||
}
|
||||
|
||||
private function getSymfonyContainerXmlPath(): string
|
||||
{
|
||||
return (string) $this->parameterProvider->provideParameter(self::SYMFONY_CONTAINER_XML_PATH_PARAMETER);
|
||||
}
|
||||
}
|
86
packages/Symfony/src/ValueObject/ServiceDefinition.php
Normal file
86
packages/Symfony/src/ValueObject/ServiceDefinition.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\ValueObject;
|
||||
|
||||
use Rector\Symfony\Contract\Tag\TagInterface;
|
||||
|
||||
final class ServiceDefinition
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $public = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $synthetic = false;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $alias;
|
||||
|
||||
/**
|
||||
* @var TagInterface[]
|
||||
*/
|
||||
private $tags = [];
|
||||
|
||||
/**
|
||||
* @param TagInterface[] $tags
|
||||
*/
|
||||
public function __construct(string $id, ?string $class, bool $public, bool $synthetic, ?string $alias, array $tags)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->class = $class;
|
||||
$this->public = $public;
|
||||
$this->synthetic = $synthetic;
|
||||
$this->alias = $alias;
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getClass(): ?string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function isPublic(): bool
|
||||
{
|
||||
return $this->public;
|
||||
}
|
||||
|
||||
public function isSynthetic(): bool
|
||||
{
|
||||
return $this->synthetic;
|
||||
}
|
||||
|
||||
public function getAlias(): ?string
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TagInterface[]
|
||||
*/
|
||||
public function getTags(): array
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
}
|
98
packages/Symfony/src/ValueObject/ServiceMap/ServiceMap.php
Normal file
98
packages/Symfony/src/ValueObject/ServiceMap/ServiceMap.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\ValueObject\ServiceMap;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
use Rector\Symfony\ValueObject\ServiceDefinition;
|
||||
|
||||
final class ServiceMap
|
||||
{
|
||||
/**
|
||||
* @var ServiceDefinition[]
|
||||
*/
|
||||
private $services = [];
|
||||
|
||||
/**
|
||||
* @param ServiceDefinition[] $services
|
||||
*/
|
||||
public function __construct(array $services)
|
||||
{
|
||||
$this->services = $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServiceDefinition[]
|
||||
*/
|
||||
public function getServices(): array
|
||||
{
|
||||
return $this->services;
|
||||
}
|
||||
|
||||
public function hasService(string $id): bool
|
||||
{
|
||||
return isset($this->services[$id]);
|
||||
}
|
||||
|
||||
public function getService(string $id): ?ServiceDefinition
|
||||
{
|
||||
return $this->services[$id] ?? null;
|
||||
}
|
||||
|
||||
public function getServiceType(string $id): ?Type
|
||||
{
|
||||
$serviceDefinition = $this->getService($id);
|
||||
if ($serviceDefinition === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$class = $serviceDefinition->getClass();
|
||||
if ($class === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$interfaces = class_implements($class);
|
||||
|
||||
if (count($interfaces) > 0) {
|
||||
foreach ($interfaces as $interface) {
|
||||
// return first interface
|
||||
return new ObjectType($interface);
|
||||
}
|
||||
}
|
||||
|
||||
return new ObjectType($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServiceDefinition[]
|
||||
*/
|
||||
public function getServicesByTag(string $tagName): array
|
||||
{
|
||||
$servicesWithTag = [];
|
||||
|
||||
foreach ($this->services as $service) {
|
||||
foreach ($service->getTags() as $tag) {
|
||||
if ($tag->getName() !== $tagName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$servicesWithTag[] = $service;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
return $servicesWithTag;
|
||||
}
|
||||
|
||||
public function getServiceIdFromNode(Expr $expr, Scope $scope): ?string
|
||||
{
|
||||
$strings = TypeUtils::getConstantStrings($scope->getType($expr));
|
||||
|
||||
return count($strings) === 1 ? $strings[0]->getValue() : null;
|
||||
}
|
||||
}
|
36
packages/Symfony/src/ValueObject/Tag.php
Normal file
36
packages/Symfony/src/ValueObject/Tag.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\ValueObject;
|
||||
|
||||
use Rector\Symfony\Contract\Tag\TagInterface;
|
||||
|
||||
final class Tag implements TagInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
public function __construct(string $name, array $data)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
52
packages/Symfony/src/ValueObject/Tag/EventListenerTag.php
Normal file
52
packages/Symfony/src/ValueObject/Tag/EventListenerTag.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\ValueObject\Tag;
|
||||
|
||||
use Rector\Symfony\Contract\Tag\TagInterface;
|
||||
|
||||
final class EventListenerTag implements TagInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $event;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $priority;
|
||||
|
||||
public function __construct(string $event, string $method, int $priority)
|
||||
{
|
||||
$this->event = $event;
|
||||
$this->method = $method;
|
||||
$this->priority = $priority;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'kernel.event_listener';
|
||||
}
|
||||
|
||||
public function getEvent(): string
|
||||
{
|
||||
return $this->event;
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Tests\FrameworkBundle\AbstractToConstructorInjectionRectorSource;
|
||||
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager;
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslator;
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslatorInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
final class SomeKernelClass extends Kernel
|
||||
{
|
||||
public function registerBundles(): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function build(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
$containerBuilder->register('stdClass', 'stdClass');
|
||||
$containerBuilder->setAlias('some_service', 'stdClass');
|
||||
|
||||
$containerBuilder->register('translator.data_collector', SomeTranslator::class);
|
||||
$containerBuilder->setAlias('translator', 'translator.data_collector');
|
||||
$containerBuilder->setAlias(SomeTranslatorInterface::class, 'translator.data_collector');
|
||||
|
||||
$containerBuilder->register('entity.manager', SomeEntityManager::class);
|
||||
$containerBuilder->setAlias(SomeEntityManager::class, 'entity.manager');
|
||||
}
|
||||
|
||||
public function getCacheDir()
|
||||
{
|
||||
return sys_get_temp_dir() . '/_tmp';
|
||||
}
|
||||
|
||||
public function getLogDir()
|
||||
{
|
||||
return sys_get_temp_dir() . '/_tmp';
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ namespace Rector\Symfony\Tests\Rector\FrameworkBundle\ContainerGetToConstructorI
|
||||
use Iterator;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Symfony\Rector\FrameworkBundle\ContainerGetToConstructorInjectionRector;
|
||||
use Rector\Symfony\Tests\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeKernelClass;
|
||||
use Rector\Symfony\Tests\FrameworkBundle\ContainerGetToConstructorInjectionRector\Source\ContainerAwareParentClass;
|
||||
use Rector\Symfony\Tests\FrameworkBundle\ContainerGetToConstructorInjectionRector\Source\ContainerAwareParentCommand;
|
||||
use Rector\Symfony\Tests\FrameworkBundle\ContainerGetToConstructorInjectionRector\Source\ThisClassCallsMethodInConstructor;
|
||||
@ -20,7 +19,10 @@ final class ContainerGetToConstructorInjectionRectorTest extends AbstractRectorT
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->setParameter(Option::KERNEL_CLASS_PARAMETER, SomeKernelClass::class);
|
||||
$this->setParameter(
|
||||
Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER,
|
||||
__DIR__ . '/../GetToConstructorInjectionRector/xml/services.xml'
|
||||
);
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Rector\Symfony\Tests\Rector\FrameworkBundle\GetToConstructorInjectionR
|
||||
use Iterator;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Symfony\Rector\FrameworkBundle\GetToConstructorInjectionRector;
|
||||
use Rector\Symfony\Tests\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeKernelClass;
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\GetToConstructorInjectionRector\Source\GetTrait;
|
||||
use Rector\Symfony\Tests\Rector\Source\SymfonyController;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
@ -19,7 +18,7 @@ final class GetToConstructorInjectionRectorTest extends AbstractRectorTestCase
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->setParameter(Option::KERNEL_CLASS_PARAMETER, SomeKernelClass::class);
|
||||
$this->setParameter(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER, __DIR__ . '/xml/services.xml');
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<!-- ->register(id, class) -->
|
||||
<service id="stdClass" class="stdClass"></service>
|
||||
|
||||
<!-- ->setAlias(alias, id) -->
|
||||
<service id="some_service" alias="stdClass"></service>
|
||||
|
||||
<!-- ->register(id, class) -->
|
||||
<service id="translator.data_collector" class="Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslator"></service>
|
||||
|
||||
<!-- ->setAlias(alias, id) -->
|
||||
<service id="translator" alias="translator.data_collector"></service>
|
||||
|
||||
<!-- ->setAlias(alias, id) -->
|
||||
<service id="Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslatorInterface" alias="translator.data_collector"></service>
|
||||
|
||||
<!-- ->register(id, class) -->
|
||||
<service id="entity.manager" class="Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager"></service>
|
||||
</services>
|
||||
</container>
|
@ -19,12 +19,13 @@ use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\Symfony\ServiceMapProvider;
|
||||
use Rector\Symfony\ValueObject\ServiceDefinition;
|
||||
use Rector\Symfony\ValueObject\Tag\EventListenerTag;
|
||||
use Rector\ValueObject\PhpVersionFeature;
|
||||
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
|
||||
|
||||
/**
|
||||
* @see \Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\EventListenerToEventSubscriberRectorTest
|
||||
@ -66,12 +67,7 @@ final class EventListenerToEventSubscriberRector extends AbstractRector
|
||||
];
|
||||
|
||||
/**
|
||||
* @var AnalyzedApplicationContainerInterface
|
||||
*/
|
||||
private $analyzedApplicationContainer;
|
||||
|
||||
/**
|
||||
* @var mixed[][]
|
||||
* @var ServiceDefinition[][][]
|
||||
*/
|
||||
private $listenerClassesToEvents = [];
|
||||
|
||||
@ -80,9 +76,14 @@ final class EventListenerToEventSubscriberRector extends AbstractRector
|
||||
*/
|
||||
private $areListenerClassesLoaded = false;
|
||||
|
||||
public function __construct(AnalyzedApplicationContainerInterface $analyzedApplicationContainer)
|
||||
/**
|
||||
* @var ServiceMapProvider
|
||||
*/
|
||||
private $applicationServiceMapProvider;
|
||||
|
||||
public function __construct(ServiceMapProvider $applicationServiceMapProvider)
|
||||
{
|
||||
$this->analyzedApplicationContainer = $analyzedApplicationContainer;
|
||||
$this->applicationServiceMapProvider = $applicationServiceMapProvider;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
@ -182,7 +183,7 @@ PHP
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
* @return ServiceDefinition[][][]
|
||||
*/
|
||||
private function getListenerClassesToEventsToMethods(): array
|
||||
{
|
||||
@ -190,33 +191,22 @@ PHP
|
||||
return $this->listenerClassesToEvents;
|
||||
}
|
||||
|
||||
if (! $this->analyzedApplicationContainer->hasService('event_dispatcher')) {
|
||||
$this->areListenerClassesLoaded = true;
|
||||
$serviceMap = $this->applicationServiceMapProvider->provide();
|
||||
$eventListeners = $serviceMap->getServicesByTag('kernel.event_listener');
|
||||
|
||||
return [];
|
||||
}
|
||||
foreach ($eventListeners as $eventListener) {
|
||||
// skip Symfony core listeners
|
||||
if (Strings::match((string) $eventListener->getClass(), '#^(Symfony|Sensio|Doctrine)\b#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var TraceableEventDispatcher $applicationEventDispatcher */
|
||||
$applicationEventDispatcher = $this->analyzedApplicationContainer->getService('event_dispatcher');
|
||||
|
||||
foreach ($applicationEventDispatcher->getListeners() as $eventName => $listenersInEvent) {
|
||||
foreach ($listenersInEvent as $listener) {
|
||||
// must be array → class and method
|
||||
if (! is_array($listener)) {
|
||||
foreach ($eventListener->getTags() as $tag) {
|
||||
if (! $tag instanceof EventListenerTag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$listenerClass = get_class($listener[0]);
|
||||
|
||||
// skip Symfony core listeners
|
||||
if (Strings::match($listenerClass, '#^(Symfony|Sensio|Doctrine)\\\\#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$listenerPriority = $applicationEventDispatcher->getListenerPriority($eventName, $listener);
|
||||
|
||||
// group event name - method - class :)
|
||||
$this->listenerClassesToEvents[$listenerClass][$eventName][] = [$listener[1], $listenerPriority];
|
||||
$eventName = $tag->getEvent();
|
||||
$this->listenerClassesToEvents[$eventListener->getClass()][$eventName][] = $eventListener;
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,8 +227,8 @@ PHP
|
||||
$classShortName = Strings::replace($classShortName, '#^(.*?)(Listener)?$#', '$1');
|
||||
$class->name = new Identifier($classShortName . 'EventSubscriber');
|
||||
|
||||
$clasMethod = $this->createGetSubscribedEventsClassMethod($eventsToMethods);
|
||||
$class->stmts[] = $clasMethod;
|
||||
$classMethod = $this->createGetSubscribedEventsClassMethod($eventsToMethods);
|
||||
$class->stmts[] = $classMethod;
|
||||
|
||||
return $class;
|
||||
}
|
||||
@ -255,12 +245,17 @@ PHP
|
||||
$this->makeStatic($getSubscribedEventsMethod);
|
||||
|
||||
foreach ($eventsToMethods as $eventName => $methodNamesWithPriorities) {
|
||||
$eventName = $this->createEventName($eventName);
|
||||
$eventNameExpr = $this->createEventName($eventName);
|
||||
|
||||
if (count($methodNamesWithPriorities) === 1) {
|
||||
$this->createSingleMethod($methodNamesWithPriorities, $eventName, $eventsToMethodsArray);
|
||||
$this->createSingleMethod($methodNamesWithPriorities, $eventNameExpr, $eventsToMethodsArray);
|
||||
} else {
|
||||
$this->createMultipleMethods($methodNamesWithPriorities, $eventName, $eventsToMethodsArray);
|
||||
$this->createMultipleMethods(
|
||||
$methodNamesWithPriorities,
|
||||
$eventNameExpr,
|
||||
$eventsToMethodsArray,
|
||||
$eventName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,16 +286,21 @@ PHP
|
||||
|
||||
/**
|
||||
* @param ClassConstFetch|String_ $expr
|
||||
* @param mixed[] $methodNamesWithPriorities
|
||||
* @param ServiceDefinition[] $methodNamesWithPriorities
|
||||
*/
|
||||
private function createSingleMethod(
|
||||
array $methodNamesWithPriorities,
|
||||
Expr $expr,
|
||||
Array_ $eventsToMethodsArray
|
||||
): void {
|
||||
[$methodName, $priority] = $methodNamesWithPriorities[0];
|
||||
|
||||
if ($priority) {
|
||||
/** @var EventListenerTag $eventTag */
|
||||
$eventTag = $methodNamesWithPriorities[0]->getTags()[0];
|
||||
|
||||
$methodName = $eventTag->getMethod();
|
||||
$priority = $eventTag->getPriority();
|
||||
|
||||
if ($priority !== 0) {
|
||||
$methodNameWithPriorityArray = new Array_();
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new String_($methodName));
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new LNumber((int) $priority));
|
||||
@ -313,29 +313,39 @@ PHP
|
||||
|
||||
/**
|
||||
* @param ClassConstFetch|String_ $expr
|
||||
* @param mixed[] $methodNamesWithPriorities
|
||||
* @param ServiceDefinition[] $methodNamesWithPriorities
|
||||
*/
|
||||
private function createMultipleMethods(
|
||||
array $methodNamesWithPriorities,
|
||||
Expr $expr,
|
||||
Array_ $eventsToMethodsArray
|
||||
Array_ $eventsToMethodsArray,
|
||||
string $eventName
|
||||
): void {
|
||||
$multipleMethodsArray = new Array_();
|
||||
$eventItems = [];
|
||||
$alreadyUsedTags = [];
|
||||
|
||||
foreach ($methodNamesWithPriorities as $methodNamesWithPriority) {
|
||||
[$methodName, $priority] = $methodNamesWithPriority;
|
||||
foreach ($methodNamesWithPriority->getTags() as $tag) {
|
||||
if (! $tag instanceof EventListenerTag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($priority) {
|
||||
$methodNameWithPriorityArray = new Array_();
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new String_($methodName));
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new LNumber((int) $priority));
|
||||
if ($eventName !== $tag->getEvent()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$multipleMethodsArray->items[] = new ArrayItem($methodNameWithPriorityArray);
|
||||
} else {
|
||||
$multipleMethodsArray->items[] = new ArrayItem(new String_($methodName));
|
||||
if (in_array($tag, $alreadyUsedTags, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$eventItems[] = $this->createEventItem($tag);
|
||||
|
||||
$alreadyUsedTags[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$multipleMethodsArray = new Array_($eventItems);
|
||||
|
||||
$eventsToMethodsArray->items[] = new ArrayItem($multipleMethodsArray, $expr);
|
||||
}
|
||||
|
||||
@ -348,4 +358,17 @@ PHP
|
||||
$arrayMixedType = new ArrayType(new MixedType(), new MixedType(true));
|
||||
$this->docBlockManipulator->addReturnTag($classMethod, $arrayMixedType);
|
||||
}
|
||||
|
||||
private function createEventItem(EventListenerTag $eventListenerTag): ArrayItem
|
||||
{
|
||||
if ($eventListenerTag->getPriority() !== 0) {
|
||||
$methodNameWithPriorityArray = new Array_();
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new String_($eventListenerTag->getMethod()));
|
||||
$methodNameWithPriorityArray->items[] = new ArrayItem(new LNumber($eventListenerTag->getPriority()));
|
||||
|
||||
return new ArrayItem($methodNameWithPriorityArray);
|
||||
}
|
||||
|
||||
return new ArrayItem(new String_($eventListenerTag->getMethod()));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubs
|
||||
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\SymfonyCodeQuality\Rector\Class_\EventListenerToEventSubscriberRector;
|
||||
use Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Source\ListenersKernel;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class EventListenerToEventSubscriberRectorTest extends AbstractRectorTestCase
|
||||
@ -14,8 +13,11 @@ final class EventListenerToEventSubscriberRectorTest extends AbstractRectorTestC
|
||||
public function test(): void
|
||||
{
|
||||
// wtf: all test have to be in single file due to autoloading race-condigition and container creating issue of fixture
|
||||
$this->setParameter(Option::KERNEL_CLASS_PARAMETER, ListenersKernel::class);
|
||||
$this->doTestFile(__DIR__ . '/Fixture/fixture.php.inc');
|
||||
$this->setParameter(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER, __DIR__ . '/config/listener_services.xml');
|
||||
|
||||
$this->doTestFile(__DIR__ . '/Fixture/some_listener.php.inc');
|
||||
$this->doTestFile(__DIR__ . '/Fixture/with_priority_listener.php.inc');
|
||||
$this->doTestFile(__DIR__ . '/Fixture/multiple_listeners.php.inc');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
|
@ -2,20 +2,6 @@
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture;
|
||||
|
||||
class SomeListener
|
||||
{
|
||||
public function methodToBeCalled()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class WithPriorityListener
|
||||
{
|
||||
public function callMe()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleMethods
|
||||
{
|
||||
public function callMe()
|
||||
@ -37,34 +23,6 @@ class MultipleMethods
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture;
|
||||
|
||||
class SomeEventSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
public function methodToBeCalled()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return ['some_event' => 'methodToBeCalled'];
|
||||
}
|
||||
}
|
||||
|
||||
class WithPriorityEventSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
public function callMe()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return ['some_event' => ['callMe', 1540]];
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleMethodsEventSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
public function callMe()
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture;
|
||||
|
||||
class SomeListener
|
||||
{
|
||||
public function methodToBeCalled()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture;
|
||||
|
||||
class SomeEventSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
public function methodToBeCalled()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return ['some_event' => 'methodToBeCalled'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture;
|
||||
|
||||
class WithPriorityListener
|
||||
{
|
||||
public function callMe()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture;
|
||||
|
||||
class WithPriorityEventSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
public function callMe()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return ['some_event' => ['callMe', 1540]];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Source;
|
||||
|
||||
use Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\MultipleCallsListener;
|
||||
use Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\MultipleMethods;
|
||||
use Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\SomeListener;
|
||||
use Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\WithPriorityListener;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
final class ListenersKernel extends Kernel
|
||||
{
|
||||
public function registerBundles(): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function build(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
$eventDispatcherDefinition = $containerBuilder->register('event_dispatcher', EventDispatcher::class);
|
||||
|
||||
$this->registerSimpleListener($containerBuilder, $eventDispatcherDefinition);
|
||||
$this->registerWithPriority($containerBuilder, $eventDispatcherDefinition);
|
||||
$this->registerMultiple($containerBuilder, $eventDispatcherDefinition);
|
||||
}
|
||||
|
||||
public function getCacheDir()
|
||||
{
|
||||
return sys_get_temp_dir() . '/_tmp';
|
||||
}
|
||||
|
||||
public function getLogDir()
|
||||
{
|
||||
return sys_get_temp_dir() . '/_tmp';
|
||||
}
|
||||
|
||||
private function registerSimpleListener(ContainerBuilder $containerBuilder, Definition $eventDispatcherDefinition): void
|
||||
{
|
||||
$containerBuilder->register(SomeListener::class);
|
||||
|
||||
/* @see \Symfony\Component\EventDispatcher\EventDispatcher::addListener() */
|
||||
$eventDispatcherDefinition->addMethodCall(
|
||||
'addListener',
|
||||
['some_event', [new Reference(SomeListener::class), 'methodToBeCalled']]
|
||||
);
|
||||
}
|
||||
|
||||
private function registerWithPriority(ContainerBuilder $containerBuilder, Definition $eventDispatcherDefinition): void
|
||||
{
|
||||
$containerBuilder->register(WithPriorityListener::class);
|
||||
|
||||
/* @see \Symfony\Component\EventDispatcher\EventDispatcher::addListener() */
|
||||
$eventDispatcherDefinition->addMethodCall(
|
||||
'addListener',
|
||||
['some_event', [new Reference(WithPriorityListener::class), 'callMe'], 1540]
|
||||
);
|
||||
}
|
||||
|
||||
private function registerMultiple(ContainerBuilder $containerBuilder, Definition $eventDispatcherDefinition): void
|
||||
{
|
||||
$containerBuilder->register(MultipleMethods::class);
|
||||
|
||||
/* @see \Symfony\Component\EventDispatcher\EventDispatcher::addListener() */
|
||||
$eventDispatcherDefinition->addMethodCall(
|
||||
'addListener',
|
||||
['single_event', [new Reference(MultipleMethods::class), 'singles']]
|
||||
);
|
||||
|
||||
$eventDispatcherDefinition->addMethodCall(
|
||||
'addListener',
|
||||
['multi_event', [new Reference(MultipleMethods::class), 'callMe']]
|
||||
);
|
||||
|
||||
$eventDispatcherDefinition->addMethodCall(
|
||||
'addListener',
|
||||
['multi_event', [new Reference(MultipleMethods::class), 'meToo']]
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="first_listener" class="Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\SomeListener">
|
||||
<tag name="kernel.event_listener" event="some_event" method="methodToBeCalled"/>
|
||||
</service>
|
||||
|
||||
<service id="second_listener" class="Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\WithPriorityListener">
|
||||
<tag name="kernel.event_listener" event="some_event" method="callMe" priority="1540" />
|
||||
</service>
|
||||
|
||||
<service id="third_listener" class="Rector\SymfonyCodeQuality\Tests\Rector\Class_\EventListenerToEventSubscriberRector\Fixture\MultipleMethods">
|
||||
<tag name="kernel.event_listener" event="single_event" method="singles"/>
|
||||
<tag name="kernel.event_listener" event="multi_event" method="callMe"/>
|
||||
<tag name="kernel.event_listener" event="multi_event" method="meToo"/>
|
||||
</service>
|
||||
|
||||
<service id="event_dispatcher" class="Symfony\Component\EventDispatcher\EventDispatcher"/>
|
||||
</services>
|
||||
</container>
|
@ -227,3 +227,12 @@ parameters:
|
||||
-
|
||||
message: '#Call to function in_array\(\) with arguments PhpParser\\Node\\Expr\\Variable, array\(\) and true will always evaluate to false#'
|
||||
path: packages/Php56/src/Rector/FunctionLike/AddDefaultValueForUndefinedVariableRector.php
|
||||
|
||||
# known values
|
||||
-
|
||||
message: '#Method Rector\\Rector\\Property\\InjectAnnotationClassRector\:\:resolveJMSDIInjectType\(\) should return PHPStan\\Type\\Type but returns PHPStan\\Type\\Type\|null#'
|
||||
path: src/Rector/Property/InjectAnnotationClassRector.php
|
||||
|
||||
-
|
||||
message: '#Method Rector\\Symfony\\Rector\\FrameworkBundle\\AbstractToConstructorInjectionRector\:\:getServiceTypeFromMethodCallArgument\(\) should return PHPStan\\Type\\Type but returns PHPStan\\Type\\Type\|null#'
|
||||
path: packages/Symfony/src/Rector/FrameworkBundle/AbstractToConstructorInjectionRector.php
|
||||
|
@ -1,9 +1,6 @@
|
||||
imports:
|
||||
- { resource: "create-rector.yaml", ignore_errors: true }
|
||||
|
||||
services:
|
||||
Rector\PSR4\Rector\Namespace_\NormalizeNamespaceByPSR4ComposerAutoloadRector: ~
|
||||
|
||||
parameters:
|
||||
exclude_paths:
|
||||
- "/Fixture/"
|
||||
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Bridge\Contract;
|
||||
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
interface AnalyzedApplicationContainerInterface
|
||||
{
|
||||
public function getTypeForName(string $name): Type;
|
||||
|
||||
public function hasService(string $name): bool;
|
||||
|
||||
/**
|
||||
* @return object
|
||||
*/
|
||||
public function getService(string $name);
|
||||
}
|
@ -26,16 +26,6 @@ final class Option
|
||||
*/
|
||||
public const OPTION_OUTPUT_FORMAT = 'output-format';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const KERNEL_CLASS_PARAMETER = 'kernel_class';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const KERNEL_ENVIRONMENT_PARAMETER = 'kernel_environment';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@ -70,4 +60,9 @@ final class Option
|
||||
* @var string
|
||||
*/
|
||||
public const MATCH_GIT_DIFF = 'match-git-diff';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SYMFONY_CONTAINER_XML_PATH_PARAMETER = 'symfony_container_xml_path';
|
||||
}
|
||||
|
@ -10,11 +10,11 @@ use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\Configuration\Rector\Architecture\DependencyInjection\VariablesToPropertyFetchCollection;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\Symfony\ServiceMapProvider;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\ActionInjectionToConstructorInjectionRectorTest
|
||||
@ -27,16 +27,16 @@ final class ActionInjectionToConstructorInjectionRector extends AbstractRector
|
||||
private $variablesToPropertyFetchCollection;
|
||||
|
||||
/**
|
||||
* @var AnalyzedApplicationContainerInterface
|
||||
* @var ServiceMapProvider
|
||||
*/
|
||||
private $analyzedApplicationContainer;
|
||||
private $applicationServiceMapProvider;
|
||||
|
||||
public function __construct(
|
||||
VariablesToPropertyFetchCollection $variablesToPropertyFetchCollection,
|
||||
AnalyzedApplicationContainerInterface $analyzedApplicationContainer
|
||||
ServiceMapProvider $applicationServiceMapProvider
|
||||
) {
|
||||
$this->variablesToPropertyFetchCollection = $variablesToPropertyFetchCollection;
|
||||
$this->analyzedApplicationContainer = $analyzedApplicationContainer;
|
||||
$this->applicationServiceMapProvider = $applicationServiceMapProvider;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
@ -49,7 +49,7 @@ final class SomeController
|
||||
public function default(ProductRepository $productRepository)
|
||||
{
|
||||
$products = $productRepository->fetchAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
@ -64,11 +64,11 @@ final class SomeController
|
||||
{
|
||||
$this->productRepository = $productRepository;
|
||||
}
|
||||
|
||||
|
||||
public function default()
|
||||
{
|
||||
$products = $this->productRepository->fetchAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
PHP
|
||||
),
|
||||
@ -135,7 +135,8 @@ PHP
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $typehint */
|
||||
return $this->analyzedApplicationContainer->hasService($paramStaticType->getClassName());
|
||||
$serviceMap = $this->applicationServiceMapProvider->provide();
|
||||
|
||||
return $serviceMap->hasService($paramStaticType->getClassName());
|
||||
}
|
||||
}
|
||||
|
@ -11,19 +11,19 @@ use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Application\ErrorAndDiffCollector;
|
||||
use Rector\BetterPhpDocParser\PhpDocNode\JMS\JMSInjectTagValueNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocNode\PHPDI\PHPDIInjectTagValueNode;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\Symfony\ServiceMapProvider;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @see https://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations#inject
|
||||
@ -40,11 +40,6 @@ final class InjectAnnotationClassRector extends AbstractRector
|
||||
JMSInject::class => JMSInjectTagValueNode::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var AnalyzedApplicationContainerInterface
|
||||
*/
|
||||
private $analyzedApplicationContainer;
|
||||
|
||||
/**
|
||||
* @var ErrorAndDiffCollector
|
||||
*/
|
||||
@ -55,17 +50,22 @@ final class InjectAnnotationClassRector extends AbstractRector
|
||||
*/
|
||||
private $annotationClasses = [];
|
||||
|
||||
/**
|
||||
* @var ServiceMapProvider
|
||||
*/
|
||||
private $serviceMapProvider;
|
||||
|
||||
/**
|
||||
* @param string[] $annotationClasses
|
||||
*/
|
||||
public function __construct(
|
||||
AnalyzedApplicationContainerInterface $analyzedApplicationContainer,
|
||||
ServiceMapProvider $serviceMapProvider,
|
||||
ErrorAndDiffCollector $errorAndDiffCollector,
|
||||
array $annotationClasses = []
|
||||
) {
|
||||
$this->analyzedApplicationContainer = $analyzedApplicationContainer;
|
||||
$this->errorAndDiffCollector = $errorAndDiffCollector;
|
||||
$this->annotationClasses = $annotationClasses;
|
||||
$this->serviceMapProvider = $serviceMapProvider;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
@ -199,14 +199,19 @@ PHP
|
||||
|
||||
private function resolveJMSDIInjectType(Node $node, JMSInjectTagValueNode $jmsInjectTagValueNode): Type
|
||||
{
|
||||
$serviceMap = $this->serviceMapProvider->provide();
|
||||
$serviceName = $jmsInjectTagValueNode->getServiceName();
|
||||
|
||||
if ($serviceName) {
|
||||
try {
|
||||
if ($this->analyzedApplicationContainer->hasService($serviceName)) {
|
||||
return $this->analyzedApplicationContainer->getTypeForName($serviceName);
|
||||
if (class_exists($serviceName)) {
|
||||
return new ObjectType($serviceName);
|
||||
}
|
||||
|
||||
if ($serviceMap->hasService($serviceName)) {
|
||||
$serviceType = $serviceMap->getServiceType($serviceName);
|
||||
if ($serviceType !== null) {
|
||||
return $serviceType;
|
||||
}
|
||||
} catch (Throwable $throwable) {
|
||||
// resolve later in errorAndDiffCollector if @var not found
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ use Rector\Configuration\Option;
|
||||
use Rector\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector;
|
||||
use Rector\Rector\Architecture\DependencyInjection\ReplaceVariableByPropertyFetchRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\SomeKernelClass;
|
||||
|
||||
final class ActionInjectionToConstructorInjectionRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
@ -18,7 +17,8 @@ final class ActionInjectionToConstructorInjectionRectorTest extends AbstractRect
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->setParameter(Option::KERNEL_CLASS_PARAMETER, SomeKernelClass::class);
|
||||
$this->setParameter(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER, __DIR__ . '/xml/services.xml');
|
||||
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source;
|
||||
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
final class SomeKernelClass extends Kernel
|
||||
{
|
||||
public function registerBundles(): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function build(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
$containerBuilder->register(ProductRepository::class);
|
||||
}
|
||||
|
||||
public function getCacheDir()
|
||||
{
|
||||
return sys_get_temp_dir() . '/_tmp';
|
||||
}
|
||||
|
||||
public function getLogDir()
|
||||
{
|
||||
return sys_get_temp_dir() . '/_tmp';
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\ProductRepository"></service>
|
||||
</services>
|
||||
</container>
|
@ -9,7 +9,6 @@ use Iterator;
|
||||
use JMS\DiExtraBundle\Annotation\Inject;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Rector\Property\InjectAnnotationClassRector;
|
||||
use Rector\Symfony\Tests\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeKernelClass;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class InjectAnnotationClassRectorTest extends AbstractRectorTestCase
|
||||
@ -19,7 +18,11 @@ final class InjectAnnotationClassRectorTest extends AbstractRectorTestCase
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->setParameter(Option::KERNEL_CLASS_PARAMETER, SomeKernelClass::class);
|
||||
$this->setParameter(
|
||||
Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER,
|
||||
__DIR__ . '/../../../../packages/Symfony/tests/Rector/FrameworkBundle/GetToConstructorInjectionRector/xml/services.xml'
|
||||
);
|
||||
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user