mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-05 02:40:50 +00:00
decouple ClassPropertyCollector, [NamedServicesToContstructor] change mechanism to add new properties post traverse
This commit is contained in:
parent
01a5fd8b41
commit
80314eb924
26
src/Builder/Class_/ClassPropertyCollector.php
Normal file
26
src/Builder/Class_/ClassPropertyCollector.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\Buillder\Class_;
|
||||||
|
|
||||||
|
final class ClassPropertyCollector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string[][]
|
||||||
|
*/
|
||||||
|
private $classProperties = [];
|
||||||
|
|
||||||
|
public function addPropertyForClass(string $class, string $propertyType, string $propertyName): void
|
||||||
|
{
|
||||||
|
$this->classProperties[$class] = [
|
||||||
|
$propertyType => $propertyName
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getPropertiesforClass(string $class): array
|
||||||
|
{
|
||||||
|
return $this->classProperties[$class] ?: [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\NodeVisitor\DependencyInjection\NamedServicesToConstructor;
|
||||||
|
|
||||||
|
use PhpParser\Node\Stmt\Class_;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
use Rector\Builder\ConstructorMethodBuilder;
|
||||||
|
use Rector\Builder\PropertyBuilder;
|
||||||
|
use Rector\Buillder\Class_\ClassPropertyCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new propertis to class and to contructor.
|
||||||
|
*/
|
||||||
|
final class AddPropertiesToClassNodeVisitor extends NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ConstructorMethodBuilder
|
||||||
|
*/
|
||||||
|
private $constructorMethodBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PropertyBuilder
|
||||||
|
*/
|
||||||
|
private $propertyBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassPropertyCollector
|
||||||
|
*/
|
||||||
|
private $newClassPropertyCollector;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ConstructorMethodBuilder $constructorMethodBuilder,
|
||||||
|
PropertyBuilder $propertyBuilder,
|
||||||
|
ClassPropertyCollector $newClassPropertyCollector
|
||||||
|
) {
|
||||||
|
$this->constructorMethodBuilder = $constructorMethodBuilder;
|
||||||
|
$this->propertyBuilder = $propertyBuilder;
|
||||||
|
$this->newClassPropertyCollector = $newClassPropertyCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterTraverse(array $nodes): array
|
||||||
|
{
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
if ($node instanceof Class_) {
|
||||||
|
$this->className = (string) $node->name;
|
||||||
|
$this->reconstruct($node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function reconstruct(Class_ $classNode): void
|
||||||
|
{
|
||||||
|
$propertiesForClass = $this->newClassPropertyCollector->getPropertiesforClass($this->className);
|
||||||
|
|
||||||
|
foreach ($propertiesForClass as $propertyType => $propertyName) {
|
||||||
|
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName);
|
||||||
|
$this->propertyBuilder->addPropertyToClass($classNode, $propertyType, $propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,17 +11,23 @@ use PhpParser\Node\Scalar\String_;
|
||||||
use PhpParser\NodeVisitorAbstract;
|
use PhpParser\NodeVisitorAbstract;
|
||||||
use Rector\Builder\Kernel\ServiceFromKernelResolver;
|
use Rector\Builder\Kernel\ServiceFromKernelResolver;
|
||||||
use Rector\Builder\Naming\NameResolver;
|
use Rector\Builder\Naming\NameResolver;
|
||||||
|
use Rector\Buillder\Class_\ClassPropertyCollector;
|
||||||
use Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor\Source\LocalKernel;
|
use Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor\Source\LocalKernel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts all:
|
* Converts all:
|
||||||
* $this->get('some_service')
|
* $this->get('some_service') # where "some_service" is name of the service in container
|
||||||
*
|
*
|
||||||
* into:
|
* into:
|
||||||
* $this->someService
|
* $this->someService # where "someService" is type of the service
|
||||||
*/
|
*/
|
||||||
final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract
|
final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $className;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var NameResolver
|
* @var NameResolver
|
||||||
*/
|
*/
|
||||||
|
@ -32,10 +38,34 @@ final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract
|
||||||
*/
|
*/
|
||||||
private $serviceFromKernelResolver;
|
private $serviceFromKernelResolver;
|
||||||
|
|
||||||
public function __construct(NameResolver $nameResolver, ServiceFromKernelResolver $serviceFromKernelResolver)
|
/**
|
||||||
{
|
* @var ClassPropertyCollector
|
||||||
|
*/
|
||||||
|
private $classPropertyCollector;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
NameResolver $nameResolver,
|
||||||
|
ServiceFromKernelResolver $serviceFromKernelResolver,
|
||||||
|
ClassPropertyCollector $classPropertyCollector
|
||||||
|
) {
|
||||||
$this->nameResolver = $nameResolver;
|
$this->nameResolver = $nameResolver;
|
||||||
$this->serviceFromKernelResolver = $serviceFromKernelResolver;
|
$this->serviceFromKernelResolver = $serviceFromKernelResolver;
|
||||||
|
$this->classPropertyCollector = $classPropertyCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node[] $nodes
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function beforeTraverse(array $nodes): ?array
|
||||||
|
{
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
if ($node instanceof Node\Stmt\Class_) {
|
||||||
|
$this->className = (string) $node->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,27 +118,17 @@ final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract
|
||||||
private function reconstruct(Node $assignOrMethodCallNode): void
|
private function reconstruct(Node $assignOrMethodCallNode): void
|
||||||
{
|
{
|
||||||
if ($assignOrMethodCallNode instanceof Assign) {
|
if ($assignOrMethodCallNode instanceof Assign) {
|
||||||
$this->processAssignment($assignOrMethodCallNode);
|
$refactoredMethodCall = $this->processMethodCallNode($assignOrMethodCallNode->expr);
|
||||||
|
if ($refactoredMethodCall) {
|
||||||
|
$assignOrMethodCallNode->expr = $refactoredMethodCall;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($assignOrMethodCallNode instanceof MethodCall) {
|
if ($assignOrMethodCallNode instanceof MethodCall) {
|
||||||
$this->processMethodCall($assignOrMethodCallNode);
|
$refactoredMethodCall = $this->processMethodCallNode($assignOrMethodCallNode->var);
|
||||||
}
|
if ($refactoredMethodCall) {
|
||||||
}
|
$assignOrMethodCallNode->var = $refactoredMethodCall;
|
||||||
|
}
|
||||||
private function processAssignment(Assign $assignNode): void
|
|
||||||
{
|
|
||||||
$refactoredMethodCall = $this->processMethodCallNode($assignNode->expr);
|
|
||||||
if ($refactoredMethodCall) {
|
|
||||||
$assignNode->expr = $refactoredMethodCall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processMethodCall(MethodCall $methodCallNode): void
|
|
||||||
{
|
|
||||||
$refactoredMethodCall = $this->processMethodCallNode($methodCallNode->var);
|
|
||||||
if ($refactoredMethodCall) {
|
|
||||||
$methodCallNode->var = $refactoredMethodCall;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,8 +162,15 @@ final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract
|
||||||
$serviceName, LocalKernel::class
|
$serviceName, LocalKernel::class
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($serviceType === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType);
|
$propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType);
|
||||||
|
|
||||||
|
$this->classPropertyCollector->addPropertyForClass($this->className, $serviceType, $propertyName);
|
||||||
|
|
||||||
return $this->createPropertyFetch($propertyName);
|
return $this->createPropertyFetch($propertyName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,203 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Rector\NodeVisitor\DependencyInjection\NamedServicesToConstructor;
|
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
use PhpParser\Node\Expr\Assign;
|
|
||||||
use PhpParser\Node\Expr\MethodCall;
|
|
||||||
use PhpParser\Node\Expr\PropertyFetch;
|
|
||||||
use PhpParser\Node\Scalar\String_;
|
|
||||||
use PhpParser\Node\Stmt\Class_;
|
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
|
||||||
use PhpParser\NodeVisitorAbstract;
|
|
||||||
use Rector\Builder\ConstructorMethodBuilder;
|
|
||||||
use Rector\Builder\Kernel\ServiceFromKernelResolver;
|
|
||||||
use Rector\Builder\Naming\NameResolver;
|
|
||||||
use Rector\Builder\PropertyBuilder;
|
|
||||||
use Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor\Source\LocalKernel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add property to class...
|
|
||||||
* Add property to constructor...
|
|
||||||
*
|
|
||||||
* How to dettect that particular class?
|
|
||||||
*/
|
|
||||||
final class NamedServicesToConstructorNodeVisitor extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var ConstructorMethodBuilder
|
|
||||||
*/
|
|
||||||
private $constructorMethodBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var PropertyBuilder
|
|
||||||
*/
|
|
||||||
private $propertyBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var NameResolver
|
|
||||||
*/
|
|
||||||
private $nameResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ServiceFromKernelResolver
|
|
||||||
*/
|
|
||||||
private $serviceFromKernelResolver;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
ConstructorMethodBuilder $constructorMethodBuilder,
|
|
||||||
PropertyBuilder $propertyBuilder,
|
|
||||||
NameResolver $nameResolver,
|
|
||||||
ServiceFromKernelResolver $serviceFromKernelResolver
|
|
||||||
) {
|
|
||||||
$this->constructorMethodBuilder = $constructorMethodBuilder;
|
|
||||||
$this->propertyBuilder = $propertyBuilder;
|
|
||||||
$this->nameResolver = $nameResolver;
|
|
||||||
$this->serviceFromKernelResolver = $serviceFromKernelResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function isCandidate(Node $node): bool
|
|
||||||
{
|
|
||||||
return $node instanceof Class_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Class_ $classNode
|
|
||||||
*/
|
|
||||||
public function reconstruct(Node $classNode): void
|
|
||||||
{
|
|
||||||
foreach ($classNode->stmts as $insideClassNode) {
|
|
||||||
// 1. Detect method
|
|
||||||
if (! $insideClassNode instanceof ClassMethod) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$methodNode = $insideClassNode;
|
|
||||||
foreach ($methodNode->stmts as $insideMethodNode) {
|
|
||||||
$insideMethodNode = $insideMethodNode->expr;
|
|
||||||
|
|
||||||
if ($insideMethodNode instanceof MethodCall && $insideMethodNode->var instanceof MethodCall) {
|
|
||||||
$this->processOnServiceMethodCall($classNode, $insideMethodNode);
|
|
||||||
|
|
||||||
// B. Find $var = $this->get('...');
|
|
||||||
} elseif ($insideMethodNode instanceof Assign) {
|
|
||||||
$this->processAssignment($classNode, $insideMethodNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processOnServiceMethodCall(Class_ $classNode, MethodCall $methodCallNode): void
|
|
||||||
{
|
|
||||||
if (! $this->isContainerGetCall($methodCallNode)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$refactoredMethodCall = $this->processMethodCallNode($classNode, $methodCallNode->var);
|
|
||||||
if ($refactoredMethodCall) {
|
|
||||||
$methodCallNode->var = $refactoredMethodCall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processAssignment(Class_ $classNode, Assign $assignNode): void
|
|
||||||
{
|
|
||||||
if (!$this->isContainerGetCall($assignNode)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->processMethodCallNode($classNode, $assignNode->expr);
|
|
||||||
|
|
||||||
/*$refactoredMethodCall = */
|
|
||||||
// if ($refactoredMethodCall) {
|
|
||||||
// $assignNode->expr = $refactoredMethodCall;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accept only "$this->get('string')" statements.
|
|
||||||
*/
|
|
||||||
private function isContainerGetCall(Node $node): bool
|
|
||||||
{
|
|
||||||
if ($node instanceof Assign && ($node->expr instanceof MethodCall || $node->var instanceof MethodCall)) {
|
|
||||||
$methodCall = $node->expr;
|
|
||||||
} elseif ($node instanceof MethodCall && $node->var instanceof MethodCall) {
|
|
||||||
$methodCall = $node->var;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($methodCall->var->name !== 'this') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((string) $methodCall->name !== 'get') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $methodCall->args[0]->value instanceof String_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processMethodCallNode(Class_ $classNode, MethodCall $methodCall): ?PropertyFetch
|
|
||||||
{
|
|
||||||
/** @var String_ $argument */
|
|
||||||
$argument = $methodCall->args[0]->value;
|
|
||||||
$serviceName = $argument->value;
|
|
||||||
|
|
||||||
$serviceType = $this->serviceFromKernelResolver->resolveServiceClassByNameFromKernel(
|
|
||||||
$serviceName, LocalKernel::class
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($serviceType === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get property name
|
|
||||||
$propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType);
|
|
||||||
|
|
||||||
// Add property assignment to constructor
|
|
||||||
$this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $serviceType, $propertyName);
|
|
||||||
|
|
||||||
// 5. Add property to class
|
|
||||||
$this->propertyBuilder->addPropertyToClass($classNode, $serviceType, $propertyName);
|
|
||||||
|
|
||||||
// creates "$this->propertyName"
|
|
||||||
// return new PropertyFetch(
|
|
||||||
// new Variable('this', [
|
|
||||||
// 'name' => $propertyName
|
|
||||||
// ]), $propertyName
|
|
||||||
// );
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when entering a node.
|
|
||||||
*
|
|
||||||
* Return value semantics:
|
|
||||||
* * null
|
|
||||||
* => $node stays as-is
|
|
||||||
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
|
|
||||||
* => Children of $node are not traversed. $node stays as-is
|
|
||||||
* * NodeTraverser::STOP_TRAVERSAL
|
|
||||||
* => Traversal is aborted. $node stays as-is
|
|
||||||
* * otherwise
|
|
||||||
* => $node is set to the return value
|
|
||||||
*
|
|
||||||
* @param Node $node Node
|
|
||||||
*
|
|
||||||
* @return null|int|Node Replacement node (or special return value)
|
|
||||||
*/
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($this->isCandidate($node)) {
|
|
||||||
$this->reconstruct($node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
parameters:
|
||||||
|
# todo
|
||||||
|
kernel_class: # for name based service refactoring
|
||||||
|
|
||||||
services:
|
services:
|
||||||
_defaults:
|
_defaults:
|
||||||
autowire: true
|
autowire: true
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor;
|
namespace Rector\Tests\NodeVisitor\DependencyInjection\NamedServicesToConstructorReconstructor;
|
||||||
|
|
||||||
use Rector\NodeVisitor\DependencyInjection\NamedServicesToConstructor\NamedServicesToConstructorNodeVisitor;
|
|
||||||
use Rector\Testing\PHPUnit\AbstractReconstructorTestCase;
|
use Rector\Testing\PHPUnit\AbstractReconstructorTestCase;
|
||||||
|
|
||||||
final class Test extends AbstractReconstructorTestCase
|
final class Test extends AbstractReconstructorTestCase
|
||||||
|
@ -13,9 +12,4 @@ final class Test extends AbstractReconstructorTestCase
|
||||||
$this->doTestFileMatchesExpectedContent(__DIR__ . '/wrong/wrong2.php.inc', __DIR__ . '/correct/correct2.php.inc');
|
$this->doTestFileMatchesExpectedContent(__DIR__ . '/wrong/wrong2.php.inc', __DIR__ . '/correct/correct2.php.inc');
|
||||||
// $this->doTestFileMatchesExpectedContent(__DIR__ . '/wrong/wrong3.php.inc', __DIR__ . '/correct/correct3.php.inc');
|
// $this->doTestFileMatchesExpectedContent(__DIR__ . '/wrong/wrong3.php.inc', __DIR__ . '/correct/correct3.php.inc');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getNodeVisitorClass(): string
|
|
||||||
{
|
|
||||||
return NamedServicesToConstructorNodeVisitor::class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
class ClassWitNamedService extends Controller
|
class ClassWithNamedService extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var stdClass
|
* @var stdClass
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
class ClassWitNamedService implements ContainerAwareInterface
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var stdClass
|
* @var stdClass
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
class ClassWitNamedService implements ContainerAwareInterface
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var stdClass
|
* @var stdClass
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
class ClassWitNamedService extends Controller
|
class ClassWithNamedService extends Controller
|
||||||
{
|
{
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
class ClassWitNamedService implements ContainerAwareInterface
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
{
|
{
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare (strict_types=1);
|
<?php declare (strict_types=1);
|
||||||
|
|
||||||
class ClassWitNamedService implements ContainerAwareInterface
|
class ClassWithNamedService implements ContainerAwareInterface
|
||||||
{
|
{
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user