Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make traverser return a new instance for each file #107

Merged
merged 2 commits into from
Sep 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/Scoper/PhpScoper.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ final class PhpScoper implements Scoper
private $decoratedScoper;
private $traverserFactory;

public function __construct(Parser $parser, Scoper $decoratedScoper)
public function __construct(Parser $parser, Scoper $decoratedScoper, TraverserFactory $traverserFactory)
{
$this->parser = $parser;
$this->decoratedScoper = $decoratedScoper;
$this->traverserFactory = new TraverserFactory();
$this->traverserFactory = $traverserFactory;
}

/**
Expand All @@ -51,9 +51,10 @@ public function scope(string $filePath, string $prefix, array $patchers, array $

$content = file_get_contents($filePath);

$statements = $this->parser->parse($content);

$traverser = $this->traverserFactory->create($prefix, $whitelist, $globalWhitelister);

$statements = $this->parser->parse($content);
$statements = $traverser->traverse($statements);

$prettyPrinter = new Standard();
Expand Down
31 changes: 14 additions & 17 deletions src/Scoper/TraverserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
use PhpParser\NodeTraverser;
use PhpParser\NodeTraverserInterface;

final class TraverserFactory
/**
* @final
*/
class TraverserFactory
{
/**
* Functions for which the arguments will be prefixed.
Expand All @@ -31,8 +34,6 @@ final class TraverserFactory
'interface_exists',
];

private $traverser;

/**
* @param string $prefix Prefix to apply to the files.
* @param string[] $whitelist List of classes to exclude from the scoping.
Expand All @@ -44,31 +45,27 @@ final class TraverserFactory
*/
public function create(string $prefix, array $whitelist, callable $globalWhitelister): NodeTraverserInterface
{
if (null !== $this->traverser) {
return $this->traverser;
}

$this->traverser = new NodeTraverser();
$traverser = new NodeTraverser();

$namespaceStatements = new NamespaceStmtCollection();
$useStatements = new UseStmtCollection();

$nameResolver = new FullyQualifiedNameResolver($namespaceStatements, $useStatements);

$this->traverser->addVisitor(new NodeVisitor\UseStmt\GroupUseStmtTransformer());
$traverser->addVisitor(new NodeVisitor\UseStmt\GroupUseStmtTransformer());

$this->traverser->addVisitor(new NodeVisitor\AppendParentNode());
$traverser->addVisitor(new NodeVisitor\AppendParentNode());

$this->traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements, $whitelist));
$traverser->addVisitor(new NodeVisitor\NamespaceStmtPrefixer($prefix, $namespaceStatements, $whitelist));

$this->traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtCollector($namespaceStatements, $useStatements));
$this->traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $globalWhitelister));
$traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtCollector($namespaceStatements, $useStatements));
$traverser->addVisitor(new NodeVisitor\UseStmt\UseStmtPrefixer($prefix, $whitelist, $globalWhitelister));

$this->traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $whitelist, $globalWhitelister, $nameResolver));
$this->traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $globalWhitelister, $nameResolver));
$traverser->addVisitor(new NodeVisitor\NameStmtPrefixer($prefix, $whitelist, $globalWhitelister, $nameResolver));
$traverser->addVisitor(new NodeVisitor\StringScalarPrefixer($prefix, self::WHITELISTED_FUNCTIONS, $whitelist, $globalWhitelister, $nameResolver));

$this->traverser->addVisitor(new NodeVisitor\WhitelistedClassAppender($whitelist));
$traverser->addVisitor(new NodeVisitor\WhitelistedClassAppender($whitelist));

return $this->traverser;
return $traverser;
}
}
4 changes: 3 additions & 1 deletion src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Humbug\PhpScoper\Scoper\NullScoper;
use Humbug\PhpScoper\Scoper\PatchScoper;
use Humbug\PhpScoper\Scoper\PhpScoper;
use Humbug\PhpScoper\Scoper\TraverserFactory;
use Humbug\SelfUpdate\Exception\RuntimeException as SelfUpdateRuntimeException;
use Humbug\SelfUpdate\Updater;
use PackageVersions\Versions;
Expand Down Expand Up @@ -100,7 +101,8 @@ function create_scoper(): Scoper
new InstalledPackagesScoper(
new PhpScoper(
create_parser(),
new NullScoper()
new NullScoper(),
new TraverserFactory()
)
)
)
Expand Down
148 changes: 145 additions & 3 deletions tests/Scoper/PhpScoperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
use Generator;
use Humbug\PhpScoper\PhpParser\FakeParser;
use Humbug\PhpScoper\Scoper;
use LogicException;
use PhpParser\Error as PhpParserError;
use PhpParser\Node\Name;
use PhpParser\NodeTraverserInterface;
use PhpParser\Parser;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
Expand Down Expand Up @@ -60,14 +64,45 @@ class PhpScoperTest extends TestCase
*/
private $decoratedScoper;

/**
* @var TraverserFactory|ObjectProphecy
*/
private $traverserFactoryProphecy;

/**
* @var TraverserFactory
*/
private $traverserFactory;

/**
* @var NodeTraverserInterface|ObjectProphecy
*/
private $traverserProphecy;

/**
* @var NodeTraverserInterface
*/
private $traverser;

/**
* @var Parser|ObjectProphecy
*/
private $parserProphecy;

/**
* @var Parser
*/
private $parser;

/**
* @inheritdoc
*/
public function setUp()
{
$this->scoper = new PhpScoper(
create_parser(),
new FakeScoper()
new FakeScoper(),
new TraverserFactory()
);

if (null === $this->tmp) {
Expand All @@ -78,6 +113,15 @@ public function setUp()
$this->decoratedScoperProphecy = $this->prophesize(Scoper::class);
$this->decoratedScoper = $this->decoratedScoperProphecy->reveal();

$this->traverserFactoryProphecy = $this->prophesize(TraverserFactory::class);
$this->traverserFactory = $this->traverserFactoryProphecy->reveal();

$this->traverserProphecy = $this->prophesize(NodeTraverserInterface::class);
$this->traverser = $this->traverserProphecy->reveal();

$this->parserProphecy = $this->prophesize(Parser::class);
$this->parser = $this->parserProphecy->reveal();

chdir($this->tmp);
}

Expand Down Expand Up @@ -136,9 +180,15 @@ public function test_does_not_scope_file_if_is_not_a_PHP_file()
)
;

$this->traverserFactoryProphecy
->create(Argument::cetera())
->willThrow(new LogicException('Unexpected call.'))
;

$scoper = new PhpScoper(
new FakeParser(),
$this->decoratedScoper
$this->decoratedScoper,
$this->traverserFactory
);

$actual = $scoper->scope($filePath, $prefix, $patchers, $whitelist, $whitelister);
Expand Down Expand Up @@ -209,9 +259,15 @@ public function test_does_not_scope_a_non_PHP_binary_files()
)
;

$this->traverserFactoryProphecy
->create(Argument::cetera())
->willThrow(new LogicException('Unexpected call.'))
;

$scoper = new PhpScoper(
new FakeParser(),
$this->decoratedScoper
$this->decoratedScoper,
$this->traverserFactory
);

$actual = $scoper->scope($filePath, $prefix, $patchers, $whitelist, $whitelister);
Expand Down Expand Up @@ -253,6 +309,92 @@ public function test_cannot_scope_an_invalid_PHP_file()
}
}

public function test_creates_a_new_traverser_for_each_file()
{
$files = [
'file1.php' => 'file1',
'file2.php' => 'file2',
];

$prefix = 'Humbug';
$patchers = [create_fake_patcher()];
$whitelist = ['Foo'];
$whitelister = create_fake_whitelister();

$this->decoratedScoperProphecy
->scope(Argument::any(), $prefix, $patchers, $whitelist, $whitelister)
->willReturn(
$expected = 'Scoped content'
)
;

$this->parserProphecy
->parse('file1')
->willReturn($file1Stmts = [
new Name('file1'),
])
;
$this->parserProphecy
->parse('file2')
->willReturn($file2Stmts = [
new Name('file2'),
])
;

/** @var NodeTraverserInterface|ObjectProphecy $firstTraverserProphecy */
$firstTraverserProphecy = $this->prophesize(NodeTraverserInterface::class);
/** @var NodeTraverserInterface $firstTraverser */
$firstTraverser = $firstTraverserProphecy->reveal();

/** @var NodeTraverserInterface|ObjectProphecy $secondTraverserProphecy */
$secondTraverserProphecy = $this->prophesize(NodeTraverserInterface::class);
/** @var NodeTraverserInterface $secondTraverser */
$secondTraverser = $secondTraverserProphecy->reveal();

$i = 0;
$this->traverserFactoryProphecy
->create($prefix, $whitelist, Argument::that(
function (...$args) use (&$i): bool {
++$i;

return 1 === $i;
}
))
->willReturn($firstTraverser)
;
$this->traverserFactoryProphecy
->create($prefix, $whitelist, Argument::that(
function (...$args) use (&$i): bool {
++$i;

return 4 === $i;
}
))
->willReturn($secondTraverser)
;

$firstTraverserProphecy->traverse($file1Stmts)->willReturn([]);
$secondTraverserProphecy->traverse($file2Stmts)->willReturn([]);

$scoper = new PhpScoper(
$this->parser,
new FakeScoper(),
$this->traverserFactory
);

foreach ($files as $file => $content) {
touch($file);
file_put_contents($file, $content);

$scoper->scope($file, $prefix, $patchers, $whitelist, $whitelister);
}

$this->parserProphecy->parse(Argument::cetera())->shouldHaveBeenCalledTimes(2);
$this->traverserFactoryProphecy->create(Argument::cetera())->shouldHaveBeenCalledTimes(2);
$firstTraverserProphecy->traverse(Argument::cetera())->shouldHaveBeenCalledTimes(1);
$secondTraverserProphecy->traverse(Argument::cetera())->shouldHaveBeenCalledTimes(1);
}

/**
* @dataProvider provideValidFiles
*/
Expand Down
41 changes: 41 additions & 0 deletions tests/Scoper/TraverserFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

/*
* This file is part of the humbug/php-scoper package.
*
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
* Pádraic Brady <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Humbug\PhpScoper\Scoper;

use Humbug\PhpScoper\Scoper;
use PHPUnit\Framework\TestCase;
use function Humbug\PhpScoper\create_fake_whitelister;

/**
* @covers \Humbug\PhpScoper\Scoper\TraverserFactory
*/
class TraverserFactoryTest extends TestCase
{
public function test_creates_a_new_traverser_at_each_call()
{
$prefix = 'Humbug';

$whitelist = ['Foo'];

$whitelister = create_fake_whitelister();

$traverserFactory = new TraverserFactory();

$firstTraverser = $traverserFactory->create($prefix, $whitelist, $whitelister);
$secondTraverser = $traverserFactory->create($prefix, $whitelist, $whitelister);

$this->assertNotSame($firstTraverser, $secondTraverser);
}
}