2021-05-10 00:23:30 +00:00
< ? php
declare ( strict_types = 1 );
namespace Rector\Symfony\Rector\Return_ ;
use PhpParser\Node ;
use PhpParser\Node\Arg ;
use PhpParser\Node\Expr\Array_ ;
use PhpParser\Node\Expr\ArrayItem ;
use PhpParser\Node\Expr\New_ ;
use PhpParser\Node\Name\FullyQualified ;
use PhpParser\Node\Scalar\String_ ;
use PhpParser\Node\Stmt\ClassMethod ;
use PhpParser\Node\Stmt\Return_ ;
use PHPStan\Analyser\Scope ;
use PHPStan\Reflection\ClassReflection ;
use PHPStan\Type\ObjectType ;
use PHPStan\Type\Type ;
use Rector\Core\Rector\AbstractRector ;
use Rector\NodeTypeResolver\Node\AttributeKey ;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
/**
* Covers https :// twig . symfony . com / doc / 1. x / deprecated . html #function
*
* @ see \Rector\Symfony\Tests\Rector\Return_\SimpleFunctionAndFilterRector\SimpleFunctionAndFilterRectorTest
*/
2021-05-10 22:23:08 +00:00
final class SimpleFunctionAndFilterRector extends \Rector\Core\Rector\AbstractRector
2021-05-10 00:23:30 +00:00
{
/**
* @ var array < string , class - string >>
*/
private const OLD_TO_NEW_CLASSES = [ 'Twig_Function_Method' => 'Twig_SimpleFunction' , 'Twig_Filter_Method' => 'Twig_SimpleFilter' ];
2021-05-10 22:23:08 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2021-05-10 00:23:30 +00:00
{
2021-05-10 22:23:08 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Changes Twig_Function_Method to Twig_SimpleFunction calls in Twig_Extension.' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ( <<< 'CODE_SAMPLE'
2021-05-10 00:23:30 +00:00
class SomeExtension extends Twig_Extension
{
public function getFunctions ()
{
return [
'is_mobile' => new Twig_Function_Method ( $this , 'isMobile' ),
];
}
public function getFilters ()
{
return [
'is_mobile' => new Twig_Filter_Method ( $this , 'isMobile' ),
];
}
}
CODE_SAMPLE
, <<< 'CODE_SAMPLE'
class SomeExtension extends Twig_Extension
{
public function getFunctions ()
{
return [
new Twig_SimpleFunction ( 'is_mobile' , [ $this , 'isMobile' ]),
];
}
public function getFilters ()
{
return [
new Twig_SimpleFilter ( 'is_mobile' , [ $this , 'isMobile' ]),
];
}
}
CODE_SAMPLE
)]);
}
/**
* @ return array < class - string < Node >>
*/
public function getNodeTypes () : array
{
2021-05-10 22:23:08 +00:00
return [ \PhpParser\Node\Stmt\Return_ :: class ];
2021-05-10 00:23:30 +00:00
}
/**
2021-12-10 10:22:23 +00:00
* @ param Return_ $node
2021-05-10 00:23:30 +00:00
*/
2021-12-10 10:22:23 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2021-05-10 00:23:30 +00:00
{
if ( $node -> expr === null ) {
return null ;
}
/** @var Scope $scope */
2021-05-10 22:23:08 +00:00
$scope = $node -> getAttribute ( \Rector\NodeTypeResolver\Node\AttributeKey :: SCOPE );
2021-05-10 00:23:30 +00:00
/** @var ClassReflection $classReflection */
$classReflection = $scope -> getClassReflection ();
if ( ! $classReflection -> isSubclassOf ( 'Twig_Extension' )) {
return null ;
}
2021-11-06 14:25:01 +00:00
$classMethod = $this -> betterNodeFinder -> findParentType ( $node , \PhpParser\Node\Stmt\ClassMethod :: class );
2021-05-10 22:23:08 +00:00
if ( ! $classMethod instanceof \PhpParser\Node\Stmt\ClassMethod ) {
2021-05-10 00:23:30 +00:00
return null ;
}
if ( ! $this -> nodeNameResolver -> isNames ( $classMethod , [ 'getFunctions' , 'getFilters' ])) {
return null ;
}
2021-05-10 22:23:08 +00:00
$this -> traverseNodesWithCallable ( $node -> expr , function ( \PhpParser\Node $node ) : ? Node {
if ( ! $node instanceof \PhpParser\Node\Expr\ArrayItem ) {
2021-05-10 00:23:30 +00:00
return null ;
}
2021-05-10 22:23:08 +00:00
if ( ! $node -> value instanceof \PhpParser\Node\Expr\New_ ) {
2021-05-10 00:23:30 +00:00
return null ;
}
2021-10-07 19:40:12 +00:00
$newObjectType = $this -> nodeTypeResolver -> getType ( $node -> value );
2021-05-10 00:23:30 +00:00
$this -> processArrayItem ( $node , $newObjectType );
return $node ;
});
return $node ;
}
2021-05-10 22:23:08 +00:00
private function processArrayItem ( \PhpParser\Node\Expr\ArrayItem $arrayItem , \PHPStan\Type\Type $newNodeType ) : void
2021-05-10 00:23:30 +00:00
{
foreach ( self :: OLD_TO_NEW_CLASSES as $oldClass => $newClass ) {
2021-05-10 22:23:08 +00:00
$oldClassObjectType = new \PHPStan\Type\ObjectType ( $oldClass );
2021-05-10 00:23:30 +00:00
if ( ! $oldClassObjectType -> equals ( $newNodeType )) {
continue ;
}
2021-05-10 22:23:08 +00:00
if ( ! $arrayItem -> key instanceof \PhpParser\Node\Scalar\String_ ) {
2021-05-10 00:23:30 +00:00
continue ;
}
2021-05-10 22:23:08 +00:00
if ( ! $arrayItem -> value instanceof \PhpParser\Node\Expr\New_ ) {
2021-05-10 00:23:30 +00:00
continue ;
}
// match!
$filterName = $this -> valueResolver -> getValue ( $arrayItem -> key );
$arrayItem -> key = null ;
2021-05-10 22:23:08 +00:00
$arrayItem -> value -> class = new \PhpParser\Node\Name\FullyQualified ( $newClass );
2022-01-07 00:57:38 +00:00
$oldArguments = $arrayItem -> value -> getArgs ();
2021-05-10 00:23:30 +00:00
$this -> decorateArrayItem ( $arrayItem , $oldArguments , $filterName );
break ;
}
}
/**
* @ param Arg [] $oldArguments
*/
2021-05-10 22:23:08 +00:00
private function decorateArrayItem ( \PhpParser\Node\Expr\ArrayItem $arrayItem , array $oldArguments , string $filterName ) : void
2021-05-10 00:23:30 +00:00
{
/** @var New_ $new */
$new = $arrayItem -> value ;
2021-05-10 22:23:08 +00:00
if ( $oldArguments [ 0 ] -> value instanceof \PhpParser\Node\Expr\Array_ ) {
2021-05-10 00:23:30 +00:00
// already array, just shift it
2021-05-10 22:23:08 +00:00
$new -> args = \array_merge ([ new \PhpParser\Node\Arg ( new \PhpParser\Node\Scalar\String_ ( $filterName ))], $oldArguments );
2021-05-10 00:23:30 +00:00
return ;
}
// not array yet, wrap to one
$arrayItems = [];
foreach ( $oldArguments as $oldArgument ) {
2021-05-10 22:23:08 +00:00
$arrayItems [] = new \PhpParser\Node\Expr\ArrayItem ( $oldArgument -> value );
2021-05-10 00:23:30 +00:00
}
2021-05-10 22:23:08 +00:00
$new -> args [ 0 ] = new \PhpParser\Node\Arg ( new \PhpParser\Node\Scalar\String_ ( $filterName ));
$new -> args [ 1 ] = new \PhpParser\Node\Arg ( new \PhpParser\Node\Expr\Array_ ( $arrayItems ));
2021-05-10 00:23:30 +00:00
}
}