2
0
forked from Wavyzz/dolibarr
Files
dolibarr-fork/dev/tools/rector/src/Renaming/GlobalToFunction.php
2024-07-03 19:35:06 +02:00

433 lines
11 KiB
PHP

<?php
namespace Dolibarr\Rector\Renaming;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\BinaryOp\Equal;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Empty_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Isset_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use Rector\Core\NodeManipulator\BinaryOpManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Php71\ValueObject\TwoNodeMatch;
use Symplify\RuleDocGenerator\Exception\PoorDocumentationException;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use PhpParser\Node\Expr\BinaryOp\NotEqual;
use PhpParser\Node\Expr\BinaryOp\Greater;
use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual;
use PhpParser\Node\Expr\BinaryOp\Smaller;
use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
/**
* Class with Rector custom rule to fix code
*/
class GlobalToFunction extends AbstractRector
{
/**
* @var \Rector\Core\NodeManipulator\BinaryOpManipulator
*/
private $binaryOpManipulator;
/**
* Constructor
*
* @param BinaryOpManipulator $binaryOpManipulator The $binaryOpManipulator
*/
public function __construct(BinaryOpManipulator $binaryOpManipulator)
{
$this->binaryOpManipulator = $binaryOpManipulator;
}
/**
* getRuleDefinition
*
* @return RuleDefinition
* @throws PoorDocumentationException
*/
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Change $conf->global to getDolGlobal in context (1) conf->global Operator Value or (2) function(conf->global...)',
[new CodeSample(
'$conf->global->CONSTANT',
'getDolGlobalInt(\'CONSTANT\')'
)]
);
}
/**
* Return a node type from https://github.com/rectorphp/php-parser-nodes-docs/
*
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class, FuncCall::class, MethodCall::class, Equal::class, NotEqual::class, Greater::class, GreaterOrEqual::class, Smaller::class, SmallerOrEqual::class, NotIdentical::class, BooleanAnd::class, Concat::class, ArrayItem::class, ArrayDimFetch::class];
}
/**
* refactor
*
* @param Node $node A node
* @return FuncCall|Equal|Concat|ArrayDimFetch|void
* return $node unchanged or void to do nothing
*/
public function refactor(Node $node)
{
if ($node instanceof Node\Expr\Assign) {
// var is left of = and expr is right
if (!isset($node->var)) {
return;
}
if ($this->isGlobalVar($node->expr)) {
$constName = $this->getConstName($node->expr);
if (empty($constName)) {
return;
}
$node->expr = new FuncCall(
new Name('getDolGlobalString'),
[new Arg($constName)]
);
}
return $node;
}
if ($node instanceof Node\Expr\ArrayItem) {
if (!isset($node->key)) {
return;
}
if ($this->isGlobalVar($node->value)) {
$constName = $this->getConstName($node->value);
if (empty($constName)) {
return;
}
$node->value = new FuncCall(
new Name('getDolGlobalString'),
[new Arg($constName)]
);
}
return $node;
}
if ($node instanceof Node\Expr\ArrayDimFetch) {
if (!isset($node->dim)) {
return;
}
if ($this->isGlobalVar($node->dim)) {
$constName = $this->getConstName($node->dim);
if (empty($constName)) {
return;
}
$node->dim = new FuncCall(
new Name('getDolGlobalString'),
[new Arg($constName)]
);
}
return $node;
}
if ($node instanceof FuncCall) {
$tmpfunctionname = $this->getName($node);
// If function is ok. We must avoid a lot of cases like isset(), empty()
if (in_array($tmpfunctionname, array('dol_escape_htmltag', 'dol_hash', 'explode', 'is_numeric', 'length_accountg', 'length_accounta', 'make_substitutions', 'min', 'max', 'trunc', 'urlencode', 'yn'))) {
//print "tmpfunctionname=".$tmpfunctionname."\n";
$args = $node->getArgs();
$nbofparam = count($args);
if ($nbofparam >= 1) {
$tmpargs = $args;
foreach ($args as $key => $arg) { // only 1 element in this array
//var_dump($key);
//var_dump($arg->value);exit;
if ($this->isGlobalVar($arg->value)) {
$constName = $this->getConstName($arg->value);
if (empty($constName)) {
return;
}
$a = new FuncCall(new Name('getDolGlobalString'), [new Arg($constName)]);
$tmpargs[$key] = new Arg($a);
$r = new FuncCall(new Name($tmpfunctionname), $tmpargs);
return $r;
}
}
}
}
return $node;
}
if ($node instanceof MethodCall) {
$tmpmethodname = $this->getName($node->name);
// If function is ok. We must avoid a lot of cases
if (in_array($tmpmethodname, array('fetch', 'idate', 'sanitize', 'select_language', 'trans'))) {
//print "tmpmethodname=".$tmpmethodname."\n";
$expr = $node->var;
$args = $node->getArgs();
$nbofparam = count($args);
if ($nbofparam >= 1) {
$tmpargs = $args;
foreach ($args as $key => $arg) { // only 1 element in this array
//var_dump($key);
//var_dump($arg->value);exit;
if ($this->isGlobalVar($arg->value)) {
$constName = $this->getConstName($arg->value);
if (empty($constName)) {
return;
}
$a = new FuncCall(new Name('getDolGlobalString'), [new Arg($constName)]);
$tmpargs[$key] = new Arg($a);
$r = new MethodCall($expr, $tmpmethodname, $tmpargs);
return $r;
}
}
}
}
return $node;
}
if ($node instanceof Concat) {
if ($this->isGlobalVar($node->left)) {
$constName = $this->getConstName($node->left);
if (empty($constName)) {
return;
}
$leftConcat = new FuncCall(
new Name('getDolGlobalString'),
[new Arg($constName)]
);
$rightConcat = $node->right;
}
if ($this->isGlobalVar($node->right)) {
$constName = $this->getConstName($node->right);
if (empty($constName)) {
return;
}
$rightConcat = new FuncCall(
new Name('getDolGlobalString'),
[new Arg($constName)]
);
$leftConcat = $node->left;
}
if (!isset($leftConcat, $rightConcat)) {
return;
}
return new Concat($leftConcat, $rightConcat);
}
if ($node instanceof BooleanAnd) {
$nodes = $this->resolveTwoNodeMatch($node);
if (!isset($nodes)) {
return;
}
/** @var Equal $node */
$node = $nodes->getFirstExpr();
}
// Now process all comparison like:
// $conf->global->... Operator Value
$typeofcomparison = '';
if ($node instanceof Equal) {
$typeofcomparison = 'Equal';
}
if ($node instanceof NotEqual) {
$typeofcomparison = 'NotEqual';
}
if ($node instanceof Greater) {
$typeofcomparison = 'Greater';
}
if ($node instanceof GreaterOrEqual) {
$typeofcomparison = 'GreaterOrEqual';
}
if ($node instanceof Smaller) {
$typeofcomparison = 'Smaller';
}
if ($node instanceof SmallerOrEqual) {
$typeofcomparison = 'SmallerOrEqual';
}
if ($node instanceof NotIdentical) {
$typeofcomparison = 'NotIdentical';
//var_dump($node->left);
}
if (empty($typeofcomparison)) {
return;
}
$isconfglobal = $this->isGlobalVar($node->left);
if (!$isconfglobal) {
// The left side is not conf->global->xxx, so we leave
return;
}
// Test the type after the comparison conf->global->xxx to know the name of function
$typeright = $node->right->getType();
switch ($typeright) {
case 'Scalar_LNumber':
$funcName = 'getDolGlobalInt';
break;
case 'Scalar_String':
$funcName = 'getDolGlobalString';
break;
default:
$funcName = 'getDolGlobalString';
break;
}
$constName = $this->getConstName($node->left);
if (empty($constName)) {
return;
}
if ($typeofcomparison == 'Equal') {
return new Equal(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
if ($typeofcomparison == 'NotEqual') {
return new NotEqual(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
if ($typeofcomparison == 'Greater') {
return new Greater(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
if ($typeofcomparison == 'GreaterOrEqual') {
return new GreaterOrEqual(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
if ($typeofcomparison == 'Smaller') {
return new Smaller(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
if ($typeofcomparison == 'SmallerOrEqual') {
return new SmallerOrEqual(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
if ($typeofcomparison == 'NotIdentical') {
return new NotIdentical(
new FuncCall(
new Name($funcName),
[new Arg($constName)]
),
$node->right
);
}
}
/**
* Get nodes with check empty
*
* @param BooleanAnd $booleanAnd A BooleandAnd
* @return TwoNodeMatch|null
*/
private function resolveTwoNodeMatch(BooleanAnd $booleanAnd): ?TwoNodeMatch
{
return $this->binaryOpManipulator->matchFirstAndSecondConditionNode(
$booleanAnd,
// Function to check if we are in the case $conf->global->... == $value
function (Node $node): bool {
if (!$node instanceof Equal) {
return \false;
}
return $this->isGlobalVar($node->left);
},
// !empty(...) || isset(...)
function (Node $node): bool {
if ($node instanceof BooleanNot && $node->expr instanceof Empty_) {
return $this->isGlobalVar($node->expr->expr);
}
if (!$node instanceof Isset_) {
return $this->isGlobalVar($node);
}
return \true;
}
);
}
/**
* Check if node is a global access with format conf->global->XXX
*
* @param Node $node A node
* @return bool Return true if node is conf->global->XXX
*/
private function isGlobalVar($node)
{
if (!$node instanceof PropertyFetch) {
return false;
}
if (!$this->isName($node->var, 'global')) {
return false;
}
$global = $node->var;
if (!$global instanceof PropertyFetch) {
return false;
}
if (!$this->isName($global->var, 'conf')) {
return false;
}
return true;
}
/**
* @param Node $node Node to be parsed
* @return Node|void Return the name of the constant
*/
private function getConstName($node)
{
if ($node instanceof PropertyFetch && $node->name instanceof Node\Expr) {
return $node->name;
}
$name = $this->getName($node);
if (empty($name)) {
return;
}
return new String_($name);
}
}