Файл: gapps/vendor/psy/psysh/src/Psy/CodeCleaner.php
Строк: 240
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2015 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy;
use PhpParserNodeTraverser;
use PhpParserParser;
use PhpParserPrettyPrinterStandard as Printer;
use PsyCodeCleanerAbstractClassPass;
use PsyCodeCleanerAssignThisVariablePass;
use PsyCodeCleanerCalledClassPass;
use PsyCodeCleanerCallTimePassByReferencePass;
use PsyCodeCleanerExitPass;
use PsyCodeCleanerFunctionReturnInWriteContextPass;
use PsyCodeCleanerImplicitReturnPass;
use PsyCodeCleanerInstanceOfPass;
use PsyCodeCleanerLeavePsyshAlonePass;
use PsyCodeCleanerLegacyEmptyPass;
use PsyCodeCleanerMagicConstantsPass;
use PsyCodeCleanerNamespacePass;
use PsyCodeCleanerStaticConstructorPass;
use PsyCodeCleanerStrictTypesPass;
use PsyCodeCleanerUseStatementPass;
use PsyCodeCleanerValidClassNamePass;
use PsyCodeCleanerValidConstantPass;
use PsyCodeCleanerValidFunctionNamePass;
use PsyExceptionParseErrorException;
/**
* A service to clean up user input, detect parse errors before they happen,
* and generally work around issues with the PHP code evaluation experience.
*/
class CodeCleaner
{
private $parser;
private $printer;
private $traverser;
private $namespace;
/**
* CodeCleaner constructor.
*
* @param Parser $parser A PhpParser Parser instance. One will be created if not explicitly supplied.
* @param Printer $printer A PhpParser Printer instance. One will be created if not explicitly supplied.
* @param NodeTraverser $traverser A PhpParser NodeTraverser instance. One will be created if not explicitly supplied.
*/
public function __construct(Parser $parser = null, Printer $printer = null, NodeTraverser $traverser = null)
{
if ($parser === null) {
$parserFactory = new ParserFactory();
$parser = $parserFactory->createParser();
}
$this->parser = $parser;
$this->printer = $printer ?: new Printer();
$this->traverser = $traverser ?: new NodeTraverser();
foreach ($this->getDefaultPasses() as $pass) {
$this->traverser->addVisitor($pass);
}
}
/**
* Get default CodeCleaner passes.
*
* @return array
*/
private function getDefaultPasses()
{
return array(
new AbstractClassPass(),
new AssignThisVariablePass(),
new FunctionReturnInWriteContextPass(),
new CallTimePassByReferencePass(),
new CalledClassPass(),
new InstanceOfPass(),
new LeavePsyshAlonePass(),
new LegacyEmptyPass(),
new ImplicitReturnPass(),
new UseStatementPass(), // must run before namespace and validation passes
new NamespacePass($this), // must run after the implicit return pass
new StrictTypesPass(),
new StaticConstructorPass(),
new ValidFunctionNamePass(),
new ValidClassNamePass(),
new ValidConstantPass(),
new MagicConstantsPass(),
new ExitPass(),
);
}
/**
* Clean the given array of code.
*
* @throws ParseErrorException if the code is invalid PHP, and cannot be coerced into valid PHP.
*
* @param array $codeLines
* @param bool $requireSemicolons
*
* @return string|false Cleaned PHP code, False if the input is incomplete.
*/
public function clean(array $codeLines, $requireSemicolons = false)
{
$stmts = $this->parse('<?php ' . implode(PHP_EOL, $codeLines) . PHP_EOL, $requireSemicolons);
if ($stmts === false) {
return false;
}
// Catch fatal errors before they happen
$stmts = $this->traverser->traverse($stmts);
return $this->printer->prettyPrint($stmts);
}
/**
* Set the current local namespace.
*
* @param null|array $namespace (default: null)
*
* @return null|array
*/
public function setNamespace(array $namespace = null)
{
$this->namespace = $namespace;
}
/**
* Get the current local namespace.
*
* @return null|array
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* Lex and parse a block of code.
*
* @see Parser::parse
*
* @param string $code
* @param bool $requireSemicolons
*
* @return array A set of statements
*/
protected function parse($code, $requireSemicolons = false)
{
try {
return $this->parser->parse($code);
} catch (PhpParserError $e) {
if ($this->parseErrorIsUnclosedString($e, $code)) {
return false;
}
if (!$this->parseErrorIsEOF($e)) {
throw ParseErrorException::fromParseError($e);
}
if ($requireSemicolons) {
return false;
}
try {
// Unexpected EOF, try again with an implicit semicolon
return $this->parser->parse($code . ';');
} catch (PhpParserError $e) {
return false;
}
}
}
private function parseErrorIsEOF(PhpParserError $e)
{
$msg = $e->getRawMessage();
return ($msg === 'Unexpected token EOF') || (strpos($msg, 'Syntax error, unexpected EOF') !== false);
}
/**
* A special test for unclosed single-quoted strings.
*
* Unlike (all?) other unclosed statements, single quoted strings have
* their own special beautiful snowflake syntax error just for
* themselves.
*
* @param PhpParserError $e
* @param string $code
*
* @return bool
*/
private function parseErrorIsUnclosedString(PhpParserError $e, $code)
{
if ($e->getRawMessage() !== 'Syntax error, unexpected T_ENCAPSED_AND_WHITESPACE') {
return false;
}
try {
$this->parser->parse($code . "';");
} catch (Exception $e) {
return false;
}
return true;
}
}