2020-09-15 11:29:25 +00:00
< ? php
2021-05-09 20:15:43 +00:00
declare ( strict_types = 1 );
2020-09-15 11:29:25 +00:00
namespace Rector\CodeQuality\Rector\Isset_ ;
use PhpParser\Node ;
use PhpParser\Node\Arg ;
use PhpParser\Node\Expr ;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd ;
use PhpParser\Node\Expr\BinaryOp\NotIdentical ;
use PhpParser\Node\Expr\Isset_ ;
use PhpParser\Node\Expr\PropertyFetch ;
use PhpParser\Node\Scalar\String_ ;
2021-06-28 00:00:40 +00:00
use PHPStan\Reflection\Php\PhpPropertyReflection ;
2021-02-28 07:47:48 +00:00
use PHPStan\Reflection\ReflectionProvider ;
2021-06-28 00:00:40 +00:00
use PHPStan\Type\MixedType ;
2021-02-17 10:51:32 +00:00
use PHPStan\Type\TypeWithClassName ;
2020-09-15 11:29:25 +00:00
use Rector\Core\Rector\AbstractRector ;
2021-06-28 00:00:40 +00:00
use Rector\Core\Reflection\ReflectionResolver ;
2020-11-16 17:50:38 +00:00
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2020-09-15 11:29:25 +00:00
/**
2021-03-12 22:20:25 +00:00
* @ see \Rector\Tests\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector\IssetOnPropertyObjectToPropertyExistsRectorTest
2021-06-28 00:00:40 +00:00
*
2020-09-15 11:29:25 +00:00
* @ see https :// 3 v4l . org / TI8XL Change isset on property object to property_exists () with not null check
*/
2021-05-10 22:23:08 +00:00
final class IssetOnPropertyObjectToPropertyExistsRector extends \Rector\Core\Rector\AbstractRector
2020-09-15 11:29:25 +00:00
{
2021-02-28 07:47:48 +00:00
/**
2021-08-23 00:20:32 +00:00
* @ var \PHPStan\Reflection\ReflectionProvider
2021-02-28 07:47:48 +00:00
*/
private $reflectionProvider ;
2021-06-28 00:00:40 +00:00
/**
* @ var \Rector\Core\Reflection\ReflectionResolver
*/
private $reflectionResolver ;
public function __construct ( \PHPStan\Reflection\ReflectionProvider $reflectionProvider , \Rector\Core\Reflection\ReflectionResolver $reflectionResolver )
2021-02-28 07:47:48 +00:00
{
$this -> reflectionProvider = $reflectionProvider ;
2021-06-28 00:00:40 +00:00
$this -> reflectionResolver = $reflectionResolver ;
2021-02-28 07:47:48 +00:00
}
2021-05-10 22:23:08 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2020-09-15 11:29:25 +00:00
{
2021-05-10 22:23:08 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Change isset on property object to property_exists() and not null check' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ( <<< 'CODE_SAMPLE'
2020-09-15 11:29:25 +00:00
class SomeClass
{
2021-02-20 23:02:05 +00:00
private $x ;
2020-09-15 11:29:25 +00:00
2021-02-20 23:02:05 +00:00
public function run () : void
{
isset ( $this -> x );
}
2020-09-15 11:29:25 +00:00
}
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2020-09-15 11:29:25 +00:00
class SomeClass
{
2021-02-20 23:02:05 +00:00
private $x ;
2020-09-15 11:29:25 +00:00
2021-02-20 23:02:05 +00:00
public function run () : void
{
property_exists ( $this , 'x' ) && $this -> x !== null ;
}
2020-09-15 11:29:25 +00:00
}
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
)]);
2020-09-15 11:29:25 +00:00
}
/**
2021-02-27 00:06:15 +00:00
* @ return array < class - string < Node >>
2020-09-15 11:29:25 +00:00
*/
2021-05-09 20:15:43 +00:00
public function getNodeTypes () : array
2020-09-15 11:29:25 +00:00
{
2021-05-10 22:23:08 +00:00
return [ \PhpParser\Node\Expr\Isset_ :: class ];
2020-09-15 11:29:25 +00:00
}
/**
* @ param Isset_ $node
*/
2021-05-10 22:23:08 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2020-09-15 11:29:25 +00:00
{
2020-10-20 10:50:21 +00:00
$newNodes = [];
2020-09-15 11:29:25 +00:00
foreach ( $node -> vars as $issetVar ) {
2021-05-10 22:23:08 +00:00
if ( ! $issetVar instanceof \PhpParser\Node\Expr\PropertyFetch ) {
2020-09-15 11:29:25 +00:00
continue ;
}
2021-06-28 00:00:40 +00:00
// has property PHP 7.4 type?
if ( $this -> hasPropertyTypeDeclaration ( $issetVar )) {
2020-10-20 10:50:21 +00:00
continue ;
2020-10-18 18:42:49 +00:00
}
2021-02-17 10:51:32 +00:00
$propertyFetchName = $this -> getName ( $issetVar -> name );
if ( $propertyFetchName === null ) {
2020-12-22 22:21:12 +00:00
continue ;
}
2021-10-07 17:46:41 +00:00
$propertyFetchVarType = $this -> getType ( $issetVar -> var );
2021-05-10 22:23:08 +00:00
if ( $propertyFetchVarType instanceof \PHPStan\Type\TypeWithClassName ) {
2021-05-09 20:15:43 +00:00
if ( ! $this -> reflectionProvider -> hasClass ( $propertyFetchVarType -> getClassName ())) {
2021-02-28 07:47:48 +00:00
continue ;
}
$classReflection = $this -> reflectionProvider -> getClass ( $propertyFetchVarType -> getClassName ());
2021-08-13 11:57:49 +00:00
if ( ! $classReflection -> hasProperty ( $propertyFetchName ) || $classReflection -> isBuiltIn ()) {
2021-05-09 20:15:43 +00:00
$newNodes [] = $this -> replaceToPropertyExistsWithNullCheck ( $issetVar -> var , $propertyFetchName , $issetVar );
2021-02-28 07:47:48 +00:00
} else {
$newNodes [] = $this -> createNotIdenticalToNull ( $issetVar );
}
} else {
2021-05-09 20:15:43 +00:00
$newNodes [] = $this -> replaceToPropertyExistsWithNullCheck ( $issetVar -> var , $propertyFetchName , $issetVar );
2020-10-18 18:42:49 +00:00
}
2021-01-05 16:26:39 +00:00
}
2021-02-17 10:51:32 +00:00
return $this -> nodeFactory -> createReturnBooleanAnd ( $newNodes );
2021-01-05 16:26:39 +00:00
}
2021-05-10 22:23:08 +00:00
private function replaceToPropertyExistsWithNullCheck ( \PhpParser\Node\Expr $expr , string $property , \PhpParser\Node\Expr\PropertyFetch $propertyFetch ) : \PhpParser\Node\Expr\BinaryOp\BooleanAnd
2021-05-09 20:15:43 +00:00
{
2021-05-10 22:23:08 +00:00
$args = [ new \PhpParser\Node\Arg ( $expr ), new \PhpParser\Node\Arg ( new \PhpParser\Node\Scalar\String_ ( $property ))];
2021-02-17 10:51:32 +00:00
$propertyExistsFuncCall = $this -> nodeFactory -> createFuncCall ( 'property_exists' , $args );
2021-05-10 22:23:08 +00:00
return new \PhpParser\Node\Expr\BinaryOp\BooleanAnd ( $propertyExistsFuncCall , $this -> createNotIdenticalToNull ( $propertyFetch ));
2020-09-15 11:29:25 +00:00
}
2021-05-10 22:23:08 +00:00
private function createNotIdenticalToNull ( \PhpParser\Node\Expr $expr ) : \PhpParser\Node\Expr\BinaryOp\NotIdentical
2020-10-18 18:42:49 +00:00
{
2021-05-10 22:23:08 +00:00
return new \PhpParser\Node\Expr\BinaryOp\NotIdentical ( $expr , $this -> nodeFactory -> createNull ());
2020-10-18 18:42:49 +00:00
}
2021-06-28 00:00:40 +00:00
private function hasPropertyTypeDeclaration ( \PhpParser\Node\Expr\PropertyFetch $propertyFetch ) : bool
{
$phpPropertyReflection = $this -> reflectionResolver -> resolvePropertyReflectionFromPropertyFetch ( $propertyFetch );
if ( ! $phpPropertyReflection instanceof \PHPStan\Reflection\Php\PhpPropertyReflection ) {
return \false ;
}
return ! $phpPropertyReflection -> getNativeType () instanceof \PHPStan\Type\MixedType ;
}
2020-09-15 11:29:25 +00:00
}