From fecc042c7750286b0eed8a0f9f969508e43d026f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 30 Oct 2018 01:57:15 +0100 Subject: [PATCH] Add support for the eval function (#277) Attempt to scope the content of the eval function whenever possible. Leave the expression unchanged otherwise. --- specs/eval.php | 209 +++++++++++++++++++ src/PhpParser/NodeVisitor/EvalPrefixer.php | 38 ++++ src/PhpParser/NodeVisitor/NewdocPrefixer.php | 29 +-- src/PhpParser/StringScoperPrefixer.php | 52 +++++ src/PhpParser/TraverserFactory.php | 1 + 5 files changed, 303 insertions(+), 26 deletions(-) create mode 100644 specs/eval.php create mode 100644 src/PhpParser/NodeVisitor/EvalPrefixer.php create mode 100644 src/PhpParser/StringScoperPrefixer.php diff --git a/specs/eval.php b/specs/eval.php new file mode 100644 index 00000000..c6b4a197 --- /dev/null +++ b/specs/eval.php @@ -0,0 +1,209 @@ +, + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'meta' => [ + 'title' => 'Eval', + // Default values. If not specified will be the one used + 'prefix' => 'Humbug', + 'whitelist' => [], + 'whitelist-global-constants' => false, + 'whitelist-global-classes' => false, + 'whitelist-global-functions' => false, + 'registered-classes' => [], + 'registered-functions' => [], + ], + + 'string' => <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + <<<'PHP' + [ + 'whitelist' => ['Acme\foo'], + 'registered-functions' => [ + ['Acme\foo', 'Humbug\Acme\foo'], + ], + 'payload' => <<<'PHP' +, + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Humbug\PhpScoper\PhpParser\NodeVisitor; + +use Humbug\PhpScoper\PhpParser\StringScoperPrefixer; +use PhpParser\Node; +use PhpParser\Node\Expr\Eval_; +use PhpParser\Node\Scalar\String_; +use PhpParser\NodeVisitorAbstract; + +final class EvalPrefixer extends NodeVisitorAbstract +{ + use StringScoperPrefixer; + + /** + * @inheritdoc + */ + public function enterNode(Node $node): Node + { + if ($node instanceof String_ && ParentNodeAppender::findParent($node) instanceof Eval_) { + $this->scopeStringValue($node); + } + + return $node; + } +} diff --git a/src/PhpParser/NodeVisitor/NewdocPrefixer.php b/src/PhpParser/NodeVisitor/NewdocPrefixer.php index d3d6ebf3..59f0af2e 100644 --- a/src/PhpParser/NodeVisitor/NewdocPrefixer.php +++ b/src/PhpParser/NodeVisitor/NewdocPrefixer.php @@ -14,9 +14,7 @@ namespace Humbug\PhpScoper\PhpParser\NodeVisitor; -use Humbug\PhpScoper\Scoper\PhpScoper; -use Humbug\PhpScoper\Whitelist; -use PhpParser\Error as PhpParserError; +use Humbug\PhpScoper\PhpParser\StringScoperPrefixer; use PhpParser\Node; use PhpParser\Node\Scalar\String_; use PhpParser\NodeVisitorAbstract; @@ -26,16 +24,7 @@ final class NewdocPrefixer extends NodeVisitorAbstract { - private $scoper; - private $prefix; - private $whitelist; - - public function __construct(PhpScoper $scoper, string $prefix, Whitelist $whitelist) - { - $this->scoper = $scoper; - $this->prefix = $prefix; - $this->whitelist = $whitelist; - } + use StringScoperPrefixer; /** * @inheritdoc @@ -43,19 +32,7 @@ public function __construct(PhpScoper $scoper, string $prefix, Whitelist $whitel public function enterNode(Node $node): Node { if ($node instanceof String_ && $this->isPhpNowdoc($node)) { - try { - $lastChar = substr($node->value, -1); - - $newValue = $this->scoper->scopePhp($node->value, $this->prefix, $this->whitelist); - - if ("\n" !== $lastChar) { - $newValue = substr($newValue, 0, -1); - } - - $node->value = $newValue; - } catch (PhpParserError $error) { - // Continue without scoping the heredoc which for some reasons contains invalid PHP code - } + $this->scopeStringValue($node); } return $node; diff --git a/src/PhpParser/StringScoperPrefixer.php b/src/PhpParser/StringScoperPrefixer.php new file mode 100644 index 00000000..0110c107 --- /dev/null +++ b/src/PhpParser/StringScoperPrefixer.php @@ -0,0 +1,52 @@ +, + * Pádraic Brady + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Humbug\PhpScoper\PhpParser; + +use Humbug\PhpScoper\Scoper\PhpScoper; +use Humbug\PhpScoper\Whitelist; +use PhpParser\Error as PhpParserError; +use PhpParser\Node\Scalar\String_; +use function substr; + +trait StringScoperPrefixer +{ + private $scoper; + private $prefix; + private $whitelist; + + public function __construct(PhpScoper $scoper, string $prefix, Whitelist $whitelist) + { + $this->scoper = $scoper; + $this->prefix = $prefix; + $this->whitelist = $whitelist; + } + + private function scopeStringValue(String_ $node): void + { + try { + $lastChar = substr($node->value, -1); + + $newValue = $this->scoper->scopePhp($node->value, $this->prefix, $this->whitelist); + + if ("\n" !== $lastChar) { + $newValue = substr($newValue, 0, -1); + } + + $node->value = $newValue; + } catch (PhpParserError $error) { + // Continue without scoping the heredoc which for some reasons contains invalid PHP code + } + } +} diff --git a/src/PhpParser/TraverserFactory.php b/src/PhpParser/TraverserFactory.php index 08b1d05b..83b7ff21 100644 --- a/src/PhpParser/TraverserFactory.php +++ b/src/PhpParser/TraverserFactory.php @@ -55,6 +55,7 @@ public function create(PhpScoper $scoper, string $prefix, Whitelist $whitelist): $traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $whitelist, $nameResolver, $this->reflector)); $traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, $whitelist, $this->reflector)); $traverser->addVisitor(new NodeVisitor\NewdocPrefixer($scoper, $prefix, $whitelist)); + $traverser->addVisitor(new NodeVisitor\EvalPrefixer($scoper, $prefix, $whitelist)); $traverser->addVisitor(new NodeVisitor\ClassAliasStmtAppender($prefix, $whitelist, $nameResolver)); $traverser->addVisitor(new NodeVisitor\ConstStmtReplacer($whitelist, $nameResolver));