Skip to content

Commit

Permalink
Add parser failure handling
Browse files Browse the repository at this point in the history
  • Loading branch information
adriansuter committed Jan 15, 2020
1 parent 87f7d7d commit 25fa5da
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 30 deletions.
45 changes: 22 additions & 23 deletions src/CodeConverter.php
Expand Up @@ -10,6 +10,7 @@

namespace AdrianSuter\Autoload\Override;

use Exception;
use PhpParser\Lexer;
use PhpParser\Lexer\Emulative;
use PhpParser\Node\Expr\FuncCall;
Expand All @@ -21,6 +22,7 @@
use PhpParser\Parser;
use PhpParser\Parser\Php7;
use PhpParser\PrettyPrinter\Standard;
use RuntimeException;

use function array_keys;
use function array_values;
Expand Down Expand Up @@ -61,12 +63,12 @@ class CodeConverter
protected $nodeFinder;

/**
* @param Lexer|null $lexer The PHP Lexer.
* @param Parser|null $parser The PHP Parser.
* @param NodeTraverser|null $traverser The PHP Node Traverser - make sure that the traverser has a CloningVisitor
* and a NameResolver visitor.
* @param Standard|null $printer The PHP Printer.
* @param NodeFinder|null $nodeFinder The PHP Node Finder.
* @param Lexer|null $lexer The PHP Lexer.
* @param Parser|null $parser The PHP Parser.
* @param NodeTraverser|null $traverser The PHP Node Traverser - make sure that the traverser has a CloningVisitor
* and a NameResolver visitor.
* @param Standard|null $printer The PHP Printer.
* @param NodeFinder|null $nodeFinder The PHP Node Finder.
*/
public function __construct(
?Lexer $lexer = null,
Expand All @@ -75,7 +77,12 @@ public function __construct(
?Standard $printer = null,
?NodeFinder $nodeFinder = null
) {
$this->lexer = $lexer ?? $this->defaultLexer();
if ($lexer === null) {
$lexer = new Emulative(
['usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos']]
);
}
$this->lexer = $lexer;

$this->parser = $parser ?? new Php7($this->lexer);

Expand All @@ -91,30 +98,22 @@ public function __construct(
$this->nodeFinder = $nodeFinder ?? new NodeFinder();
}

/**
* @return Lexer
*/
private function defaultLexer(): Lexer
{
return new Emulative(
[
'usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'],
]
);
}

/**
* Convert the given source code.
*
* @param string $code The source code.
* @param array $functionCallMap The function call map.
* @param string $code The source code.
* @param array $functionCallMap The function call map.
*
* @return string
*/
public function convert(string $code, array $functionCallMap): string
{
$oldStmts = $this->parser->parse($code);
$oldTokens = $this->lexer->getTokens();
try {
$oldStmts = $this->parser->parse($code);
$oldTokens = $this->lexer->getTokens();
} catch (Exception $exception) {
throw new RuntimeException('Code Converter failed to parse the code.', 0, $exception);
}

$overridePlaceholders = [];

Expand Down
2 changes: 2 additions & 0 deletions src/Override.php
Expand Up @@ -52,6 +52,8 @@ class Override
private static $converter;

/**
* Set the code converter to be used.
*
* @param CodeConverter $converter
*/
public static function setCodeConverter(CodeConverter $converter): void
Expand Down
58 changes: 57 additions & 1 deletion tests/CodeConverterTest.php
Expand Up @@ -11,16 +11,72 @@
namespace AdrianSuter\Autoload\Override\Tests;

use AdrianSuter\Autoload\Override\CodeConverter;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\NodeFinder;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use ReflectionProperty;
use RuntimeException;

class CodeConverterTest extends TestCase
{
public function testA()
public function testConvert()
{
$converter = new CodeConverter();
$this->assertEquals(
'<?php echo \foo\rand(0, 9);',
$converter->convert('<?php echo \rand(0, 9);', ['\rand' => 'foo\rand'])
);
}

public function testEmptyFunctionCallMap()
{
$converter = new CodeConverter();
$this->assertEquals(
'<?php echo \foo\rand(0, 9);',
$converter->convert('<?php echo \foo\rand(0, 9);', [])
);
}

public function testConvertIfFuncCallNameHasNoResolvedNameAttribute()
{
$nameProphecy = $this->prophesize(Name::class);
$nameHasAttributeMethodProphecy = new MethodProphecy($nameProphecy, 'hasAttribute', [Argument::any()]);
$nameHasAttributeMethodProphecy->willReturn(false);
$nameProphecy->addMethodProphecy($nameHasAttributeMethodProphecy);

$funcCallMock = $this->createMock(FuncCall::class);
$nameReflectionProperty = new ReflectionProperty(FuncCall::class, 'name');
$nameReflectionProperty->setValue($funcCallMock, $nameProphecy->reveal());

$nodeFinderProphecy = $this->prophesize(NodeFinder::class);

$nodeFinderFindInstanceOfMethodProphecy = new MethodProphecy(
$nodeFinderProphecy,
'findInstanceOf',
[Argument::any(), FuncCall::class]
);
$nodeFinderFindInstanceOfMethodProphecy->willReturn([$funcCallMock]);
$nodeFinderProphecy->addMethodProphecy($nodeFinderFindInstanceOfMethodProphecy);

/** @var NodeFinder $nodeFinder */
$nodeFinder = $nodeFinderProphecy->reveal();
$converter = new CodeConverter(null, null, null, null, $nodeFinder);

$this->assertEquals(
'<?php echo \time();',
$converter->convert('<?php echo \time();', [])
);
}

public function testInvalidCode()
{
$this->expectException(RuntimeException::class);
$this->expectErrorMessage('Code Converter failed to parse the code.');

$converter = new CodeConverter();
$converter->convert('<?php Hello World', []);
}
}
18 changes: 12 additions & 6 deletions tests/IntegrationNamespaceTest.php
Expand Up @@ -34,26 +34,32 @@ protected function getOverrideDeclarations(): array
public function testEarth()
{
$earth = new \My\Integration\TestNamespaceOverride\Earth();
// Calls substr() which is a local function in the namespace.

// Calls substr() which is a local function in the namespace.
$this->assertEquals('GFE', $earth->substrLocal());
// Calls \substr() > Overridden by FQNS-declaration.

// Calls \substr() > Overridden by FQNS-declaration.
$GLOBALS['substr_return'] = 'XYZ';
$this->assertEquals('XYZ', $earth->substrGlobal());
// Calls \time() > No override.

// Calls \time() > No override.
$GLOBALS['time_return'] = 3;
$this->assertGreaterThanOrEqual(\time(), $earth->time());
}

public function testMoon()
{
$moon = new \My\Integration\TestNamespaceOverride\Moon();
// Calls \time() > Overridden by FQCN.

// Calls \time() > Overridden by FQCN.
$GLOBALS['time_return'] = 1;
$this->assertEquals(1, $moon->time());
// Calls \time()-alias > Overridden by FQCN.

// Calls \time()-alias > Overridden by FQCN.
$GLOBALS['time_return'] = 2;
$this->assertEquals(2, $moon->timeUseAlias());
// Calls \substr() > Overridden by FQNS-declaration.

// Calls \substr() > Overridden by FQNS-declaration.
$GLOBALS['substr_return'] = 'ZZZ';
$this->assertEquals('ZZZ', $moon->substr('AAA', 0, 2));
}
Expand Down

0 comments on commit 25fa5da

Please sign in to comment.