add Sensio TemplateTagValueNode

This commit is contained in:
Tomas Votruba 2019-08-30 08:27:03 +02:00
parent 8811c900ea
commit e6de2a7d38
30 changed files with 583 additions and 265 deletions

View File

@ -87,7 +87,10 @@
"Rector\\TypeDeclaration\\": "packages/TypeDeclaration/src",
"Rector\\Utils\\DocumentationGenerator\\": "utils/DocumentationGenerator/src",
"Rector\\Utils\\RectorGenerator\\": "utils/RectorGenerator/src"
}
},
"classmap": [
"stubs"
]
},
"autoload-dev": {
"psr-4": {

View File

@ -57,6 +57,7 @@ parameters:
- '*tests/*/Fixture/*'
- '*tests/*/Expected/*'
- '*utils/ContributorTools/templates/*'
- 'stubs/*'
skip:
PHP_CodeSniffer\Standards\PSR2\Sniffs\Methods\MethodDeclarationSniff.Underscore: ~

View File

@ -282,6 +282,22 @@ final class PhpDocInfo
}
}
/**
* @param string $type
*/
public function matchChildValueNodeOfType(string $type): ?PhpDocTagValueNode
{
foreach ($this->phpDocNode->children as $phpDocChildNode) {
if ($phpDocChildNode instanceof PhpDocTagNode) {
if (is_a($phpDocChildNode->value, $type, true)) {
return $phpDocChildNode->value;
}
}
}
return null;
}
private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode
{
$phpDocNode = $this->getPhpDocNode();
@ -308,20 +324,4 @@ final class PhpDocInfo
return $phpDocTagValueNode->getAttribute(Attribute::TYPE_AS_ARRAY);
}
/**
* @param string $type
*/
private function matchChildValueNodeOfType(string $type): ?PhpDocTagValueNode
{
foreach ($this->phpDocNode->children as $phpDocChildNode) {
if ($phpDocChildNode instanceof PhpDocTagNode) {
if (is_a($phpDocChildNode->value, $type, true)) {
return $phpDocChildNode->value;
}
}
}
return null;
}
}

View File

@ -0,0 +1,65 @@
<?php declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocParser;
use Nette\Utils\Strings;
use PhpParser\Node;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\Configuration\CurrentNodeProvider;
use Rector\DoctrinePhpDocParser\AnnotationReader\NodeAnnotationReader;
abstract class AbstractPhpDocParser
{
/**
* @var NodeAnnotationReader
*/
protected $nodeAnnotationReader;
/**
* @var CurrentNodeProvider
*/
private $currentNodeProvider;
/**
* @required
*/
public function autowireAbstractPhpDocParser(
CurrentNodeProvider $currentNodeProvider,
NodeAnnotationReader $nodeAnnotationReader
): void {
$this->currentNodeProvider = $currentNodeProvider;
$this->nodeAnnotationReader = $nodeAnnotationReader;
}
protected function getCurrentPhpNode(): Node
{
return $this->currentNodeProvider->getNode();
}
protected function resolveAnnotationContent(TokenIterator $tokenIterator): string
{
$clonedTokenIterator = clone $tokenIterator;
$singleLineContent = $clonedTokenIterator->joinUntil(
Lexer::TOKEN_END,
Lexer::TOKEN_PHPDOC_EOL,
Lexer::TOKEN_CLOSE_PHPDOC
);
if ($singleLineContent === '' || Strings::match($singleLineContent, '#^\((.*?)\)$#m')) {
$annotationContent = $singleLineContent;
$tokenIterator->joinUntil(Lexer::TOKEN_END, Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC);
} else { // multiline - content
// skip all tokens for this annotation, so next annotation can work with tokens after this one
$annotationContent = $tokenIterator->joinUntil(Lexer::TOKEN_END, Lexer::TOKEN_CLOSE_PHPDOC);
}
return $this->cleanMultilineAnnotationContent($annotationContent);
}
private function cleanMultilineAnnotationContent(string $annotationContent): string
{
return Strings::replace($annotationContent, '#(\s+)\*(\s+)#m', '$1$3');
}
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocParser\Ast\PhpDoc;
use Nette\Utils\Json;
use Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpDocTagValueNode
{
use AttributeTrait;
/**
* @param mixed[] $item
*/
protected function printArrayItem(array $item, string $key): string
{
$json = Json::encode($item);
$json = Strings::replace($json, '#,#', ', ');
$json = Strings::replace($json, '#\[(.*?)\]#', '{$1}');
return sprintf('%s=%s', $key, $json);
}
}

View File

@ -5,12 +5,15 @@ namespace Rector\DoctrinePhpDocParser\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\Mapping\Annotation;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpParser\Node\Resolver\NameResolver;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
final class NodeAnnotationReader
{
@ -30,6 +33,19 @@ final class NodeAnnotationReader
$this->nameResolver = $nameResolver;
}
public function readMethodAnnotation(ClassMethod $classMethod, string $annotationClassName): Template
{
/** @var string $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
/** @var string $methodName */
$methodName = $this->nameResolver->getName($classMethod);
$reflectionMethod = new ReflectionMethod($className, $methodName);
return $this->annotationReader->getMethodAnnotation($reflectionMethod, $annotationClassName);
}
public function readDoctrineClassAnnotation(Class_ $class, string $annotationClassName): Annotation
{
$classReflection = $this->createClassReflectionFromNode($class);

View File

@ -2,35 +2,18 @@
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc;
use Nette\Utils\Json;
use Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\PhpDocParser\Ast\PhpDoc\AbstractTagValueNode;
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineTagNodeInterface;
abstract class AbstractDoctrineTagValueNode implements AttributeAwareNodeInterface, DoctrineTagNodeInterface
abstract class AbstractDoctrineTagValueNode extends AbstractTagValueNode implements DoctrineTagNodeInterface
{
use AttributeTrait;
/**
* @var string[]|null
*/
protected $orderedVisibleItems;
/**
* @param mixed[] $item
*/
protected function printArrayItem(array $item, string $key): string
{
$json = Json::encode($item);
$json = Strings::replace($json, '#,#', ', ');
$json = Strings::replace($json, '#\[(.*?)\]#', '{$1}');
return sprintf('%s=%s', $key, $json);
}
/**
* @param string[] $contentItems
*/

View File

@ -16,10 +16,8 @@ use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\Configuration\CurrentNodeProvider;
use Rector\DoctrinePhpDocParser\AnnotationReader\NodeAnnotationReader;
use Rector\BetterPhpDocParser\PhpDocParser\AbstractPhpDocParser;
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Class_\EntityTagValueNode;
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ColumnTagValueNode;
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\IdTagValueNode;
@ -34,66 +32,31 @@ use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineTagNodeInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* Parses following ORM annotations:
* - ORM\Entity
* Parses various ORM annotations
*/
final class OrmTagParser
final class OrmTagParser extends AbstractPhpDocParser
{
/**
* @var CurrentNodeProvider
*/
private $currentNodeProvider;
/**
* @var NodeAnnotationReader
*/
private $nodeAnnotationReader;
public function __construct(
CurrentNodeProvider $currentNodeProvider,
NodeAnnotationReader $nodeAnnotationReader
) {
$this->currentNodeProvider = $currentNodeProvider;
$this->nodeAnnotationReader = $nodeAnnotationReader;
}
public function parse(TokenIterator $tokenIterator, string $tag): ?PhpDocTagValueNode
{
/** @var Class_|Property $node */
$node = $this->currentNodeProvider->getNode();
/** @var Class_|Property $currentPhpNode */
$currentPhpNode = $this->getCurrentPhpNode();
$clonedTokenIterator = clone $tokenIterator;
$singleLineContent = $clonedTokenIterator->joinUntil(
Lexer::TOKEN_END,
Lexer::TOKEN_PHPDOC_EOL,
Lexer::TOKEN_CLOSE_PHPDOC
);
if ($singleLineContent === '' || Strings::match($singleLineContent, '#^\((.*?)\)$#m')) {
$annotationContent = $singleLineContent;
$tokenIterator->joinUntil(Lexer::TOKEN_END, Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC);
} else { // multiline - content
// skip all tokens for this annotation, so next annotation can work with tokens after this one
$annotationContent = $tokenIterator->joinUntil(Lexer::TOKEN_END, Lexer::TOKEN_CLOSE_PHPDOC);
}
$annotationContent = $this->cleanMultilineAnnotationContent($annotationContent);
$annotationContent = $this->resolveAnnotationContent($tokenIterator);
// Entity tags
if ($node instanceof Class_) {
if ($currentPhpNode instanceof Class_) {
if ($tag === EntityTagValueNode::SHORT_NAME) {
return $this->createEntityTagValueNode($node, $annotationContent);
return $this->createEntityTagValueNode($currentPhpNode, $annotationContent);
}
if ($tag === TableTagValueNode::SHORT_NAME) {
return $this->createTableTagValueNode($node, $annotationContent);
return $this->createTableTagValueNode($currentPhpNode, $annotationContent);
}
}
// Property tags
if ($node instanceof Property) {
return $this->createPropertyTagValueNode($tag, $node, $annotationContent);
if ($currentPhpNode instanceof Property) {
return $this->createPropertyTagValueNode($tag, $currentPhpNode, $annotationContent);
}
return null;
@ -142,20 +105,25 @@ final class OrmTagParser
return null;
}
private function createEntityTagValueNode(Class_ $node, string $annotationContent): EntityTagValueNode
private function createIdTagValueNode(): IdTagValueNode
{
return new IdTagValueNode();
}
private function createEntityTagValueNode(Class_ $class, string $annotationContent): EntityTagValueNode
{
/** @var Entity $entity */
$entity = $this->nodeAnnotationReader->readDoctrineClassAnnotation($node, Entity::class);
$entity = $this->nodeAnnotationReader->readDoctrineClassAnnotation($class, Entity::class);
return new EntityTagValueNode($entity->repositoryClass, $entity->readOnly, $this->resolveAnnotationItemsOrder(
$annotationContent
));
}
private function createTableTagValueNode(Class_ $node, string $annotationContent): TableTagValueNode
private function createTableTagValueNode(Class_ $class, string $annotationContent): TableTagValueNode
{
/** @var Table $table */
$table = $this->nodeAnnotationReader->readDoctrineClassAnnotation($node, Table::class);
$table = $this->nodeAnnotationReader->readDoctrineClassAnnotation($class, Table::class);
return new TableTagValueNode(
$table->name,
@ -348,14 +316,4 @@ final class OrmTagParser
$annotationContent
);
}
private function cleanMultilineAnnotationContent(string $annotationContent): string
{
return Strings::replace($annotationContent, '#(\s+)\*(\s+)#m', '$1$3');
}
private function createIdTagValueNode(): IdTagValueNode
{
return new IdTagValueNode();
}
}

View File

@ -5,4 +5,4 @@ services:
Rector\Sensio\:
resource: '../src/'
exclude: '../src/{Rector/**/*Rector.php}'
exclude: '../src/{Rector/**/*Rector.php,PhpDocParser/Ast/PhpDoc/*}'

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace Rector\Sensio\Extension;
use Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\PhpDocParserExtensionInterface;
use Rector\Sensio\PhpDocParser\SensioPhpDocTagParser;
final class SensioPhpDocParserExtension implements PhpDocParserExtensionInterface
{
/**
* @var SensioPhpDocTagParser
*/
private $sensioPhpDocTagParser;
public function __construct(SensioPhpDocTagParser $sensioPhpDocTagParser)
{
$this->sensioPhpDocTagParser = $sensioPhpDocTagParser;
}
public function matchTag(string $tag): bool
{
return (bool) Strings::match($tag, '#^@Template$#');
}
public function parse(TokenIterator $tokenIterator, string $tag): ?PhpDocTagValueNode
{
return $this->sensioPhpDocTagParser->parse($tokenIterator, $tag);
}
}

View File

@ -0,0 +1,76 @@
<?php declare(strict_types=1);
namespace Rector\Sensio\PhpDocParser\Ast\PhpDoc;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\PhpDocParser\Ast\PhpDoc\AbstractTagValueNode;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
final class TemplateTagValueNode extends AbstractTagValueNode
{
use AttributeTrait;
/**
* @var string
*/
public const SHORT_NAME = '@Template';
/**
* @var string
*/
public const CLASS_NAME = Template::class;
/**
* @var string|null
*/
private $template;
/**
* @var mixed[]
*/
private $owner = [];
/**
* @var mixed[]
*/
private $vars = [];
/**
* @param mixed[] $owner
* @param mixed[] $vars
*/
public function __construct(?string $template, array $owner, array $vars)
{
$this->template = $template;
$this->owner = $owner;
$this->vars = $vars;
}
public function __toString(): string
{
$contentItems = [];
if ($this->template) {
$contentItems[] = $this->template;
}
if ($this->owner) {
$contentItems[] = $this->printArrayItem($this->owner, 'owner');
}
if ($this->vars) {
$contentItems[] = $this->printArrayItem($this->vars, 'vars');
}
if ($contentItems === []) {
return '';
}
return implode(', ', $contentItems);
}
public function getTemplate(): ?string
{
return $this->template;
}
}

View File

@ -0,0 +1,60 @@
<?php declare(strict_types=1);
namespace Rector\Sensio\PhpDocParser;
use Doctrine\Common\Annotations\AnnotationException;
use Nette\Utils\Strings;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\PhpDocParser\AbstractPhpDocParser;
use Rector\Sensio\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
final class SensioPhpDocTagParser extends AbstractPhpDocParser
{
public function parse(TokenIterator $tokenIterator, string $tag): ?PhpDocTagValueNode
{
$currentPhpNode = $this->getCurrentPhpNode();
// this is needed to append tokens to the end of annotation, even if not used
$this->resolveAnnotationContent($tokenIterator);
try {
if ($currentPhpNode instanceof ClassMethod) {
if ($tag === TemplateTagValueNode::SHORT_NAME) {
return $this->createTemplateTagValueNode($currentPhpNode);
}
}
} catch (AnnotationException $annotationException) {
// this not an annotation we look for, just having the same ending
if (Strings::match(
$annotationException->getMessage(),
'#\[Semantical Error\] The annotation (.*) in (.*?) was never imported. ' .
'Did you maybe forget to add a "use" statement for this annotation?#s'
)
) {
return null;
}
throw $annotationException;
}
return null;
}
private function createTemplateTagValueNode(ClassMethod $classMethod): TemplateTagValueNode
{
/** @var Template $templateAnnotation */
$templateAnnotation = $this->nodeAnnotationReader->readMethodAnnotation(
$classMethod,
TemplateTagValueNode::CLASS_NAME
);
return new TemplateTagValueNode(
$templateAnnotation->getTemplate(),
$templateAnnotation->getOwner(),
$templateAnnotation->getVars()
);
}
}

View File

@ -2,18 +2,20 @@
namespace Rector\Sensio\Rector\FrameworkExtraBundle;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Rector\Sensio\Helper\TemplateGuesser;
use Rector\Sensio\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
final class TemplateAnnotationRector extends AbstractRector
{
@ -81,7 +83,7 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->docBlockManipulator->hasTag($node, 'Template')) {
if (! $this->docBlockManipulator->hasTag($node, TemplateTagValueNode::CLASS_NAME)) {
return null;
}
@ -103,7 +105,7 @@ CODE_SAMPLE
}
// remove annotation
$this->docBlockManipulator->removeTagFromNode($node, 'Template');
$this->docBlockManipulator->removeTagFromNode($node, TemplateTagValueNode::CLASS_NAME);
return $node;
}
@ -122,19 +124,24 @@ CODE_SAMPLE
$arguments[] = $returnNode->expr;
}
$arguments = array_merge($arguments, $this->resolveArgumentsFromMethodCall($returnNode));
$arguments = array_merge($arguments, $this->resolveArrayArgumentsFromMethodCall($returnNode));
return $this->createArgs($arguments);
}
private function resolveTemplateName(ClassMethod $classMethod): string
{
$templateTag = $this->docBlockManipulator->getTagByName($classMethod, 'Template');
$content = (string) $templateTag;
/** @var PhpDocInfo $classMethodPhpDocInfo */
$classMethodPhpDocInfo = $this->getPhpDocInfo($classMethod);
$annotationContent = Strings::match($content, '#\(("|\')(?<filename>.*?)("|\')\)#');
if (isset($annotationContent['filename'])) {
return $annotationContent['filename'];
/** @var TemplateTagValueNode|null $templateTagValueNode */
$templateTagValueNode = $classMethodPhpDocInfo->matchChildValueNodeOfType(TemplateTagValueNode::class);
if ($templateTagValueNode === null) {
throw new ShouldNotHappenException(__METHOD__);
}
if ($templateTagValueNode->getTemplate() !== null) {
return $templateTagValueNode->getTemplate();
}
return $this->templateGuesser->resolveFromClassMethodNode($classMethod, $this->version);
@ -142,17 +149,21 @@ CODE_SAMPLE
/**
* Already existing method call
* @return mixed[]
* @return Array_[]
*/
private function resolveArgumentsFromMethodCall(Return_ $returnNode): array
private function resolveArrayArgumentsFromMethodCall(Return_ $returnNode): array
{
if (! $returnNode->expr instanceof MethodCall) {
return [];
}
$arguments = [];
if ($returnNode->expr instanceof MethodCall) {
foreach ($returnNode->expr->args as $arg) {
if ($arg->value instanceof Array_) {
$arguments[] = $arg->value;
}
foreach ($returnNode->expr->args as $arg) {
if (! $arg->value instanceof Array_) {
continue;
}
$arguments[] = $arg->value;
}
return $arguments;

View File

@ -3,6 +3,7 @@
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService13Controller extends SymfonyController
{
@ -38,6 +39,7 @@ class ClassWithNamedService13Controller extends SymfonyController
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService13Controller extends SymfonyController
{

View File

@ -3,6 +3,7 @@
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService23Controller extends SymfonyController
{
@ -32,6 +33,7 @@ class ClassWithNamedService23Controller extends SymfonyController
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService23Controller extends SymfonyController
{

View File

@ -3,6 +3,7 @@
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService33Controller extends SymfonyController
{
@ -38,6 +39,7 @@ class ClassWithNamedService33Controller extends SymfonyController
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService33Controller extends SymfonyController
{

View File

@ -1,6 +1,7 @@
<?php declare (strict_types=1);
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService43 extends SymfonyController
{
@ -20,6 +21,7 @@ class ClassWithNamedService43 extends SymfonyController
<?php declare (strict_types=1);
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService43 extends SymfonyController
{

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Fixture;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
class SkipJustTemplateController extends SymfonyController
{
/**
* @Template
*/
public function indexAction()
{
return [];
}
}

View File

@ -3,6 +3,7 @@
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService15Controller extends SymfonyController
{
@ -38,6 +39,7 @@ class ClassWithNamedService15Controller extends SymfonyController
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService15Controller extends SymfonyController
{

View File

@ -3,6 +3,7 @@
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService25Controller extends SymfonyController
{
@ -32,6 +33,7 @@ class ClassWithNamedService25Controller extends SymfonyController
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService25Controller extends SymfonyController
{

View File

@ -3,6 +3,7 @@
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService35Controller extends SymfonyController
{
@ -38,6 +39,7 @@ class ClassWithNamedService35Controller extends SymfonyController
namespace AppBundle\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService35Controller extends SymfonyController
{

View File

@ -3,6 +3,7 @@
namespace App\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService45Controller extends SymfonyController
{
@ -15,15 +16,6 @@ class ClassWithNamedService45Controller extends SymfonyController
'form' => $form->createView(),
));
}
/**
* @Template()
* @Route("/", name="homepage")
*/
public function index()
{
return [];
}
}
?>
@ -33,6 +25,7 @@ class ClassWithNamedService45Controller extends SymfonyController
namespace App\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService45Controller extends SymfonyController
{
@ -42,14 +35,6 @@ class ClassWithNamedService45Controller extends SymfonyController
'form' => $form->createView(),
));
}
/**
* @Route("/", name="homepage")
*/
public function index()
{
return $this->render('class_with_named_service45/index.html.twig');
}
}
?>

View File

@ -0,0 +1,36 @@
<?php
namespace App\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService55Controller extends SymfonyController
{
/**
* @Template()
*/
public function index()
{
return [];
}
}
?>
-----
<?php
namespace App\Controller;
use Rector\Sensio\Tests\Rector\FrameworkExtraBundle\TemplateAnnotationRector\Source\SymfonyController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class ClassWithNamedService55Controller extends SymfonyController
{
public function index()
{
return $this->render('class_with_named_service55/index.html.twig');
}
}
?>

View File

@ -14,6 +14,7 @@ final class TemplateAnnotationVersion3RectorTest extends AbstractRectorTestCase
__DIR__ . '/Fixture/Version3/fixture2.php.inc',
__DIR__ . '/Fixture/Version3/fixture3.php.inc',
__DIR__ . '/Fixture/Version3/fixture4.php.inc',
__DIR__ . '/Fixture/Version3/skip_just_template.php.inc',
]);
}

View File

@ -14,6 +14,7 @@ final class TemplateAnnotationVersion5RectorTest extends AbstractRectorTestCase
__DIR__ . '/Fixture/Version5/fixture2.php.inc',
__DIR__ . '/Fixture/Version5/fixture3.php.inc',
__DIR__ . '/Fixture/Version5/fixture4.php.inc',
__DIR__ . '/Fixture/Version5/fixture5.php.inc',
]);
}

View File

@ -11,4 +11,4 @@ services:
Rector\Symfony\:
resource: '../src/'
exclude: '../src/{Rector/**/*Rector.php}'
exclude: '../src/{Rector/**/*Rector.php,PhpDocParser/Ast/PhpDoc/*}'

View File

@ -1,66 +0,0 @@
<?php declare(strict_types=1);
namespace Rector\Symfony\PhpDoc\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
final class RouteTagValueNode implements AttributeAwareNodeInterface, PhpDocTagValueNode
{
use AttributeTrait;
/**
* @var string
*/
public const SHORT_NAME = '@Route';
public function __toString(): string
{
// TODO: Implement __toString() method.
}
// public function __toString(): string
// {
// $contentItems = [];
//
// if ($this->type !== null) {
// $contentItems['type'] = sprintf('type="%s"', $this->type);
// }
//
// if ($this->name !== null) {
// $contentItems['name'] = sprintf('name="%s"', $this->name);
// }
//
// if ($this->length !== null) {
// $contentItems['length'] = sprintf('length=%s', $this->length);
// }
//
// if ($this->precision !== null) {
// $contentItems['precision'] = sprintf('precision=%s', $this->precision);
// }
//
// if ($this->scale !== null) {
// $contentItems['scale'] = sprintf('scale=%s', $this->scale);
// }
//
// if ($this->unique !== null) {
// $contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
// }
//
// if ($this->nullable !== null) {
// $contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
// }
//
// if ($this->options) {
// $contentItems['options'] = $this->printArrayItem($this->options, 'options');
// }
//
// if ($this->columnDefinition !== null) {
// $contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
// }
//
// return $this->printContentItems($contentItems);
// }
}

View File

@ -1,66 +0,0 @@
<?php declare(strict_types=1);
namespace Rector\Symfony\PhpDoc\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
final class TemplateTagValueNode implements AttributeAwareNodeInterface, PhpDocTagValueNode
{
use AttributeTrait;
/**
* @var string
*/
public const SHORT_NAME = '@Template';
public function __toString(): string
{
// TODO: Implement __toString() method.
}
// public function __toString(): string
// {
// $contentItems = [];
//
// if ($this->type !== null) {
// $contentItems['type'] = sprintf('type="%s"', $this->type);
// }
//
// if ($this->name !== null) {
// $contentItems['name'] = sprintf('name="%s"', $this->name);
// }
//
// if ($this->length !== null) {
// $contentItems['length'] = sprintf('length=%s', $this->length);
// }
//
// if ($this->precision !== null) {
// $contentItems['precision'] = sprintf('precision=%s', $this->precision);
// }
//
// if ($this->scale !== null) {
// $contentItems['scale'] = sprintf('scale=%s', $this->scale);
// }
//
// if ($this->unique !== null) {
// $contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
// }
//
// if ($this->nullable !== null) {
// $contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
// }
//
// if ($this->options) {
// $contentItems['options'] = $this->printArrayItem($this->options, 'options');
// }
//
// if ($this->columnDefinition !== null) {
// $contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
// }
//
// return $this->printContentItems($contentItems);
// }
}

View File

@ -30,6 +30,8 @@ parameters:
# part of composer
- 'tests/Composer/AutoloadWrongCasesEventSubscriber.php'
- '*/tests/Rector/Psr4/MultipleClassFileToPsr4ClassesRector/Expected/Just*ExceptionWithoutNamespace.php'
# stubs
- 'stubs/*'
ignoreErrors:
# false positive
@ -188,6 +190,7 @@ parameters:
- '#Method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:(.*?)\(\) should return Rector\\DoctrinePhpDocParser\\Ast\\PhpDoc\\(.*?)\|null but returns PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode\|null#'
- '#Call to function property_exists\(\) with string and (.*?) will always evaluate to false#'
- '#Method Rector\\Console\\Option\\SetOptionResolver\:\:separateVersionedAndUnversionedSets\(\) should return array<array<string\>\> but returns array<int, array<int\|string, array<int, string\>\|string\>\>#'
- '#Method Rector\\DoctrinePhpDocParser\\AnnotationReader\\NodeAnnotationReader\:\:readMethodAnnotation\(\) should return Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Template but returns object\|null#'
# PHP 7.4 1_000 support
- '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\) does not accept string#'

163
stubs/Sensio/Template.php Normal file
View File

@ -0,0 +1,163 @@
<?php declare(strict_types=1);
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
use RuntimeException;
if (class_exists('Sensio\Bundle\FrameworkExtraBundle\Configuration\Template')) {
return;
}
// mimics https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/Configuration/Template.php, is missing localy
/**
* @Annotation
*/
class Template
{
/**
* The template.
*
* @var string
*/
protected $template;
/**
* The associative array of template variables.
*
* @var array
*/
private $vars = [];
/**
* Should the template be streamed?
*
* @var bool
*/
private $streamable = false;
/**
* The controller (+action) this annotation is set to.
*
* @var array
*/
private $owner = [];
public function __construct(array $values)
{
foreach ($values as $k => $v) {
if (! method_exists($this, $name = 'set' . $k)) {
throw new RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, static::class));
}
$this->{$name}($v);
}
}
/**
* Returns the array of templates variables.
*
* @return array
*/
public function getVars()
{
return $this->vars;
}
/**
* @param bool $streamable
*/
public function setIsStreamable($streamable): void
{
$this->streamable = $streamable;
}
/**
* @return bool
*/
public function isStreamable()
{
return (bool) $this->streamable;
}
/**
* Sets the template variables.
*
* @param array $vars The template variables
*/
public function setVars($vars): void
{
$this->vars = $vars;
}
/**
* Sets the template logic name.
*
* @param string $template The template logic name
*/
public function setValue($template): void
{
$this->setTemplate($template);
}
/**
* Returns the template.
*
* @return string
*/
public function getTemplate()
{
return $this->template;
}
/**
* Sets the template.
*
* @param string $template The template
*/
public function setTemplate($template): void
{
$this->template = $template;
}
/**
* Returns the annotation alias name.
*
* @return string
*
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'template';
}
/**
* Only one template directive is allowed.
*
* @return bool
*
* @see ConfigurationInterface
*/
public function allowArray()
{
return false;
}
/**
* @param array $owner
*/
public function setOwner(array $owner): void
{
$this->owner = $owner;
}
/**
* The controller (+action) this annotation is attached to.
*
* @return array
*/
public function getOwner()
{
return $this->owner;
}
}