diff --git a/composer.json b/composer.json index 4f33e727ad1a3..388337e5a44aa 100644 --- a/composer.json +++ b/composer.json @@ -66,6 +66,7 @@ "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "dev-master as 1.14.6", "monolog/monolog": "^2.3", + "nikic/php-parser": "~4.4.0", "pelago/emogrifier": "^5.0.0", "php-amqplib/php-amqplib": "~3.0.0", "phpseclib/mcrypt_compat": "2.0.0", diff --git a/lib/internal/Magento/Framework/View/Template/Html/Minifier.php b/lib/internal/Magento/Framework/View/Template/Html/Minifier.php index 3eb8baec7f569..fb3f2d476dd04 100644 --- a/lib/internal/Magento/Framework/View/Template/Html/Minifier.php +++ b/lib/internal/Magento/Framework/View/Template/Html/Minifier.php @@ -9,6 +9,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; +use Magento\Framework\View\Template\Html\Minifier\Php; class Minifier implements MinifierInterface { @@ -116,41 +117,86 @@ public function minify($file) $dir = dirname($file); $fileName = basename($file); $content = $this->readFactory->create($dir)->readFile($fileName); - //Storing Heredocs - $heredocs = []; - $content = preg_replace_callback( - '/<<<([A-z]+).*?\1;/ims', - function ($match) use (&$heredocs) { - $heredocs[] = $match[0]; + $heredocs = null; - return '__MINIFIED_HEREDOC__' .(count($heredocs) - 1); - }, + // Safely minify PHP code and remove single-line PHP comments by using a parser. + if (null !== $content) { + $parser = (new \PhpParser\ParserFactory())->create(\PhpParser\ParserFactory::PREFER_PHP7); + + /** + * Prevent problems with deeply nested ASTs if Xdebug is enabled. + * @see https://github.com/nikic/PHP-Parser/blob/v4.4.0/doc/2_Usage_of_basic_components.markdown#bootstrapping + */ + $nestingLevelConfigValue = ini_get('xdebug.max_nesting_level'); + + if (false !== $nestingLevelConfigValue) { + ini_set('xdebug.max_nesting_level', '3000'); + } + + try { + $ast = $parser->parse($content); + + $traverser = new \PhpParser\NodeTraverser(); + $traverser->addVisitor(new Php\NodeVisitor()); + $ast = $traverser->traverse($ast); + + $prettyPrinter = new Php\PrettyPrinter(); + $content = $prettyPrinter->prettyPrintFile($ast); + $heredocs = $prettyPrinter->getDelayedHeredocs(); + } catch (\Error $error) { + // Some PHP code is seemingly invalid, or too complex. + } finally { + if (false !== $nestingLevelConfigValue) { + ini_set('xdebug.max_nesting_level', $nestingLevelConfigValue); + } + } + } + + // Stash the heredocs now if the template could not be parsed. + if (null === $heredocs) { + $content = preg_replace_callback( + '/<<<([A-z]+).*?\1\s*;/ims', + function ($match) use (&$heredocs) { + $heredocs[] = $match[0]; + + return '__MINIFIED_HEREDOC__' .(count($heredocs) - 1); + }, ($content ?? '') - ); + ); + } + + // Remove insignificant spaces before closing HTML tags + // (preserve one space after ]]>, and all spaces inside
and