inlineCodeParser = $inlineCodeParser; $this->anonymousFunctionFactory = $anonymousFunctionFactory; $this->reservedKeywordAnalyzer = $reservedKeywordAnalyzer; } public function provideMinPhpVersion() : int { return PhpVersionFeature::DEPRECATE_CREATE_FUNCTION; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Use anonymous functions instead of deprecated create_function()', [new CodeSample(<<<'CODE_SAMPLE' class ClassWithCreateFunction { public function run() { $callable = create_function('$matches', "return '$delimiter' . strtolower(\$matches[1]);"); } } CODE_SAMPLE , <<<'CODE_SAMPLE' class ClassWithCreateFunction { public function run() { $callable = function($matches) use ($delimiter) { return $delimiter . strtolower($matches[1]); }; } } CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [FuncCall::class]; } /** * @param FuncCall $node * @return Closure|null */ public function refactor(Node $node) : ?Node { if (!$this->isName($node, 'create_function')) { return null; } if ($node->isFirstClassCallable()) { return null; } if (\count($node->getArgs()) < 2) { return null; } $firstExpr = $node->getArgs()[0]->value; $secondExpr = $node->getArgs()[1]->value; $params = $this->createParamsFromString($firstExpr); $stmts = $this->parseStringToBody($secondExpr); $refactored = $this->anonymousFunctionFactory->create($params, $stmts, null); foreach ($refactored->uses as $key => $use) { $variableName = $this->getName($use->var); if ($variableName === null) { continue; } if ($this->reservedKeywordAnalyzer->isNativeVariable($variableName)) { unset($refactored->uses[$key]); } } return $refactored; } /** * @return Param[] */ private function createParamsFromString(Expr $expr) : array { $content = $this->inlineCodeParser->stringify($expr); $content = 'inlineCodeParser->parseString($content); /** @var Expression $expression */ $expression = $nodes[0]; /** @var Assign $assign */ $assign = $expression->expr; $function = $assign->expr; if (!$function instanceof Closure) { throw new ShouldNotHappenException(); } return $function->params; } /** * @return Stmt[] */ private function parseStringToBody(Expr $expr) : array { if (!$expr instanceof String_ && !$expr instanceof Encapsed && !$expr instanceof Concat) { // special case of code elsewhere return [$this->createEval($expr)]; } $content = $this->inlineCodeParser->stringify($expr); return $this->inlineCodeParser->parseString($content); } private function createEval(Expr $expr) : Expression { $evalFuncCall = new FuncCall(new Name('eval'), [new Arg($expr)]); return new Expression($evalFuncCall); } }