diff --git a/.gitignore b/.gitignore index 8c818d2f..11b22e01 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /fixtures/*/.box_dump/ /fixtures/*/vendor/ /fixtures/set028-symfony/expected-output +/fixtures/set030/expected-output .phpunit* /vendor/ /vendor-bin/*/vendor/ diff --git a/Makefile b/Makefile index 7a5cf150..ad57eeb0 100644 --- a/Makefile +++ b/Makefile @@ -62,13 +62,13 @@ tm: bin/phpunit .PHONY: e2e e2e: ## Run end-to-end tests -e2e: e2e_004 e2e_005 e2e_011 e2e_013 e2e_014 e2e_015 e2e_016 e2e_017 e2e_018 e2e_019 e2e_020 e2e_021 e2e_022 e2e_023 e2e_024 e2e_025 e2e_026 e2e_027 e2e_028 e2e_029 +e2e: e2e_004 e2e_005 e2e_011 e2e_013 e2e_014 e2e_015 e2e_016 e2e_017 e2e_018 e2e_019 e2e_020 e2e_021 e2e_022 e2e_023 e2e_024 e2e_025 e2e_026 e2e_027 e2e_028 e2e_029 e2e_030 PHPSCOPER=bin/php-scoper.phar .PHONY: e2e_004 e2e_004: ## Run end-to-end tests for the fixture set 004 — Source code case -e2e_004: bin/php-scoper.phar +e2e_004: $(PHPSCOPER) $(PHPBIN) $(BOX) compile --no-parallel --working-dir fixtures/set004 php build/set004/bin/greet.phar > build/set004/output @@ -76,7 +76,7 @@ e2e_004: bin/php-scoper.phar .PHONY: e2e_005 e2e_005: ## Run end-to-end tests for the fixture set 005 — Third-party code case -e2e_005: bin/php-scoper.phar fixtures/set005/vendor +e2e_005: $(PHPSCOPER) fixtures/set005/vendor $(PHPBIN) $(BOX) compile --no-parallel --working-dir fixtures/set005 php build/set005/bin/greet.phar > build/set005/output @@ -84,7 +84,7 @@ e2e_005: bin/php-scoper.phar fixtures/set005/vendor .PHONY: e2e_011 e2e_011: ## Run end-to-end tests for the fixture set 011 — Whitelist case -e2e_011: bin/php-scoper.phar fixtures/set011/vendor +e2e_011: $(PHPSCOPER) fixtures/set011/vendor $(PHPBIN) $(BOX) compile --no-parallel --working-dir fixtures/set011 cp -R fixtures/set011/tests/ build/set011/tests/ @@ -93,7 +93,7 @@ e2e_011: bin/php-scoper.phar fixtures/set011/vendor .PHONY: e2e_013 e2e_013: # Run end-to-end tests for the fixture set 013 — The init command -e2e_013: bin/php-scoper.phar +e2e_013: $(PHPSCOPER) rm -rf build/set013 cp -R fixtures/set013 build/set013 $(PHPSCOPER) init --working-dir=build/set013 --no-interaction @@ -101,7 +101,7 @@ e2e_013: bin/php-scoper.phar .PHONY: e2e_014 e2e_014: ## Run end-to-end tests for the fixture set 014 — Source code case with PSR-0 -e2e_014: bin/php-scoper.phar +e2e_014: $(PHPSCOPER) $(PHPBIN) $(BOX) compile --no-parallel --working-dir fixtures/set014 php build/set014/bin/greet.phar > build/set014/output @@ -109,7 +109,7 @@ e2e_014: bin/php-scoper.phar .PHONY: e2e_015 e2e_015: ## Run end-to-end tests for the fixture set 015 — Third-party code case with PSR-0 -e2e_015: bin/php-scoper.phar fixtures/set015/vendor +e2e_015: $(PHPSCOPER) fixtures/set015/vendor $(PHPBIN) $(BOX) compile --no-parallel --working-dir fixtures/set015 php build/set015/bin/greet.phar > build/set015/output @@ -117,7 +117,7 @@ e2e_015: bin/php-scoper.phar fixtures/set015/vendor .PHONY: e2e_016 e2e_016: ## Run end-to-end tests for the fixture set 016 — Symfony Finder -e2e_016: bin/php-scoper.phar fixtures/set016-symfony-finder/vendor +e2e_016: $(PHPSCOPER) fixtures/set016-symfony-finder/vendor $(PHPBIN) $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set016-symfony-finder \ --output-dir=../../build/set016-symfony-finder \ @@ -132,7 +132,7 @@ e2e_016: bin/php-scoper.phar fixtures/set016-symfony-finder/vendor .PHONY: e2e_017 e2e_017: ## Run end-to-end tests for the fixture set 017 — Symfony DependencyInjection -e2e_017: bin/php-scoper.phar fixtures/set017-symfony-di/vendor +e2e_017: $(PHPSCOPER) fixtures/set017-symfony-di/vendor $(PHPBIN) $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set017-symfony-di \ --output-dir=../../build/set017-symfony-di \ @@ -147,7 +147,7 @@ e2e_017: bin/php-scoper.phar fixtures/set017-symfony-di/vendor .PHONY: e2e_018 e2e_018: ## Run end-to-end tests for the fixture set 018 — Nikic PHP-Parser -e2e_018: bin/php-scoper.phar fixtures/set018-nikic-parser/vendor +e2e_018: $(PHPSCOPER) fixtures/set018-nikic-parser/vendor $(PHPBIN) $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set018-nikic-parser \ --prefix=_Prefixed \ @@ -162,7 +162,7 @@ e2e_018: bin/php-scoper.phar fixtures/set018-nikic-parser/vendor .PHONY: e2e_019 e2e_019: ## Run end-to-end tests for the fixture set 019 — Symfony Console -e2e_019: bin/php-scoper.phar fixtures/set019-symfony-console/vendor +e2e_019: $(PHPSCOPER) fixtures/set019-symfony-console/vendor $(PHPBIN) $(PHPSCOPER) add-prefix --working-dir=fixtures/set019-symfony-console \ --prefix=_Prefixed \ --output-dir=../../build/set019-symfony-console \ @@ -177,7 +177,7 @@ e2e_019: bin/php-scoper.phar fixtures/set019-symfony-console/vendor .PHONY: e2e_020 e2e_020: ## Run end-to-end tests for the fixture set 020 — Infection -e2e_020: bin/php-scoper.phar fixtures/set020-infection/vendor clover.xml +e2e_020: $(PHPSCOPER) fixtures/set020-infection/vendor clover.xml $(PHPBIN) $(PHPSCOPER) add-prefix --working-dir=fixtures/set020-infection \ --output-dir=../../build/set020-infection \ --force \ @@ -198,7 +198,7 @@ e2e_020: bin/php-scoper.phar fixtures/set020-infection/vendor clover.xml .PHONY: e2e_021 e2e_021: ## Run end-to-end tests for the fixture set 020 — Composer -e2e_021: bin/php-scoper.phar fixtures/set021-composer/vendor +e2e_021: $(PHPSCOPER) fixtures/set021-composer/vendor $(PHPBIN) $(PHPSCOPER) add-prefix --working-dir=fixtures/set021-composer \ --output-dir=../../build/set021-composer \ --force \ @@ -218,7 +218,7 @@ e2e_021: bin/php-scoper.phar fixtures/set021-composer/vendor .PHONY: e2e_022 e2e_022: ## Run end-to-end tests for the fixture set 022 — Whitelist the project code with namespace whitelisting -e2e_022: bin/php-scoper.phar fixtures/set022/vendor +e2e_022: $(PHPSCOPER) fixtures/set022/vendor $(PHPBIN) $(BOX) compile --no-parallel --working-dir fixtures/set022 cp -R fixtures/set022/tests/ build/set022/tests/ @@ -228,7 +228,7 @@ e2e_022: bin/php-scoper.phar fixtures/set022/vendor .PHONY: e2e_023 e2e_023: ## Run end-to-end tests for the fixture set 023 — Whitelisting a whole third-party component with namespace whitelisting -e2e_023: bin/php-scoper.phar fixtures/set023/vendor +e2e_023: $(PHPSCOPER) fixtures/set023/vendor $(PHPBIN) $(PHPSCOPER) add-prefix --working-dir=fixtures/set023 \ --output-dir=../../build/set023 \ --force \ @@ -241,7 +241,7 @@ e2e_023: bin/php-scoper.phar fixtures/set023/vendor .PHONY: e2e_024 e2e_024: ## Run end-to-end tests for the fixture set 024 — Whitelisting user functions registered in the global namespace -e2e_024: bin/php-scoper.phar fixtures/set024/vendor +e2e_024: $(PHPSCOPER) fixtures/set024/vendor $(PHPBIN) $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set024 \ --output-dir=../../build/set024 \ @@ -256,7 +256,7 @@ e2e_024: bin/php-scoper.phar fixtures/set024/vendor .PHONY: e2e_025 e2e_025: ## Run end-to-end tests for the fixture set 025 — Whitelisting a vendor function -e2e_025: bin/php-scoper.phar fixtures/set025/vendor +e2e_025: $(PHPSCOPER) fixtures/set025/vendor $(PHPBIN) $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set025 \ --output-dir=../../build/set025 \ @@ -270,7 +270,7 @@ e2e_025: bin/php-scoper.phar fixtures/set025/vendor .PHONY: e2e_026 e2e_026: ## Run end-to-end tests for the fixture set 026 — Whitelisting classes and functions with pattern matching -e2e_026: bin/php-scoper.phar fixtures/set026/vendor +e2e_026: $(PHPSCOPER) fixtures/set026/vendor $(PHPBIN) $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set026 \ --output-dir=../../build/set026 \ @@ -284,7 +284,7 @@ e2e_026: bin/php-scoper.phar fixtures/set026/vendor .PHONY: e2e_027 e2e_027: ## Run end-to-end tests for the fixture set 027 — Laravel -e2e_027: bin/php-scoper.phar fixtures/set027-laravel/vendor +e2e_027: $(PHPSCOPER) fixtures/set027-laravel/vendor php $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set027-laravel \ --output-dir=../../build/set027-laravel \ @@ -299,7 +299,7 @@ e2e_027: bin/php-scoper.phar fixtures/set027-laravel/vendor .PHONY: e2e_028 e2e_028: ## Run end-to-end tests for the fixture set 028 — Symfony -e2e_028: bin/php-scoper.phar fixtures/set028-symfony/vendor +e2e_028: $(PHPSCOPER) fixtures/set028-symfony/vendor php $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set028-symfony \ --output-dir=../../build/set028-symfony \ @@ -318,7 +318,7 @@ e2e_028: bin/php-scoper.phar fixtures/set028-symfony/vendor .PHONY: e2e_029 e2e_029: ## Run end-to-end tests for the fixture set 029 — EasyRdf -e2e_029: bin/php-scoper fixtures/set029-easy-rdf/vendor +e2e_029: $(PHPSCOPER) fixtures/set029-easy-rdf/vendor php $(PHPSCOPER) add-prefix \ --working-dir=fixtures/set029-easy-rdf \ --output-dir=../../build/set029-easy-rdf \ @@ -333,6 +333,25 @@ e2e_029: bin/php-scoper fixtures/set029-easy-rdf/vendor php build/set029-easy-rdf/main.php > build/set029-easy-rdf/output diff fixtures/set029-easy-rdf/expected-output build/set029-easy-rdf/output + diff fixtures/set028-symfony/expected-output build/set028-symfony/output + +.PHONY: e2e_030 +e2e_030: ## Run end-to-end tests for the fixture set 039 — global function whitelisting +e2e_030: $(PHPSCOPER) fixtures/set030/vendor + php $(PHPSCOPER) add-prefix \ + --working-dir=fixtures/set030 \ + --output-dir=../../build/set030 \ + --no-config \ + --force \ + --no-interaction \ + --stop-on-failure + + php fixtures/set030/main.php > fixtures/set030/expected-output + + composer --working-dir=build/set030 dump-autoload --no-dev + php build/set030/main.php > build/set030/output + + diff fixtures/set030/expected-output build/set030/output .PHONY: tb BLACKFIRE=blackfire @@ -439,6 +458,10 @@ fixtures/set029-easy-rdf/vendor: fixtures/set029-easy-rdf/composer.lock composer --working-dir=fixtures/set029-easy-rdf install --no-dev touch $@ +fixtures/set030/vendor: fixtures/set030/composer.json + composer --working-dir=fixtures/set030 install --no-dev + touch $@ + composer.lock: composer.json @echo composer.lock is not up to date. diff --git a/fixtures/set030/composer.json b/fixtures/set030/composer.json new file mode 100644 index 00000000..7ec32202 --- /dev/null +++ b/fixtures/set030/composer.json @@ -0,0 +1,8 @@ +{ + "autoload": { + "files": [ + "src/functions.php" + ] + }, + "bin": "main.php" +} diff --git a/fixtures/set030/expected-output b/fixtures/set030/expected-output new file mode 100644 index 00000000..79ebd086 --- /dev/null +++ b/fixtures/set030/expected-output @@ -0,0 +1,2 @@ +ok +ok diff --git a/fixtures/set030/main.php b/fixtures/set030/main.php new file mode 100644 index 00000000..bf843b52 --- /dev/null +++ b/fixtures/set030/main.php @@ -0,0 +1,16 @@ + true, + 'JSON_UNESCAPED_LINE_TERMINATORS' => true, + 'OPENSSL_DONT_ZERO_PAD_KEY' => true, + 'PHP_FD_SETSIZE' => true, + 'PHP_INT_MIN' => true, + 'PHP_OS_FAMILY' => true, + ]; + + private const KNOWN_INTERNAL_FUNCTIONS = [ + 'deflate_add' => true, + 'deflate_init' => true, + 'error_clear_last' => true, + 'ftp_append' => true, + 'hash_hkdf' => true, + 'inflate_add' => true, + 'inflate_init' => true, + 'intdiv' => true, + 'is_iterable' => true, + 'openssl_pkcs7_read' => true, + 'pcntl_signal_get_handler' => true, + 'preg_replace_callback_array' => true, + 'sapi_windows_vt100_support' => true, + 'socket_export_stream' => true, + 'spl_object_id' => true, + 'stream_isatty' => true, + 'utf8_decode' => true, + 'utf8_encode' => true, + ]; + private $classReflector; - private $functionReflector; private $constants; - public function __construct(ClassReflector $classReflector, FunctionReflector $functionReflector) + public function __construct(ClassReflector $classReflector) { $this->classReflector = $classReflector; - $this->functionReflector = $functionReflector; } public function isClassInternal(string $name): bool @@ -48,11 +88,12 @@ public function isClassInternal(string $name): bool public function isFunctionInternal(string $name): bool { + if (array_key_exists($name, self::KNOWN_INTERNAL_FUNCTIONS)) { + return true; + } + try { return (new ReflectionFunction($name))->isInternal(); - - // TODO: leverage Better Reflection instead - return $this->functionReflector->reflect($name)->isInternal(); } catch (ReflectionException $exception) { return false; } @@ -60,17 +101,25 @@ public function isFunctionInternal(string $name): bool public function isConstantInternal(string $name): bool { - if (null === $this->constants) { - $constants = get_defined_constants(true); - - unset($constants['user']); + if (array_key_exists($name, self::KNOWN_INTERNAL_CONSTANTS)) { + return true; + } - $this->constants = array_merge(...array_values($constants)); + return array_key_exists(strtoupper($name), $this->getInternalConstants()); + } - unset($constants); + private function getInternalConstants(): array + { + if (null !== $this->constants) { + return $this->constants; } - // TODO: find a better solution - return array_key_exists(strtoupper($name), $this->constants); + $constants = get_defined_constants(true); + + unset($constants['user']); + + $this->constants = array_merge(...array_values($constants)); + + return $this->constants; } } diff --git a/tests/PhpParser/TraverserFactoryTest.php b/tests/PhpParser/TraverserFactoryTest.php index 37efc684..b08caeb7 100644 --- a/tests/PhpParser/TraverserFactoryTest.php +++ b/tests/PhpParser/TraverserFactoryTest.php @@ -33,10 +33,7 @@ public function test_creates_a_new_traverser_at_each_call(): void $whitelist = Whitelist::create(true, true, true, 'Foo'); - $classReflector = new Reflector( - (new BetterReflection())->classReflector(), - (new BetterReflection())->functionReflector() - ); + $classReflector = new Reflector((new BetterReflection())->classReflector()); $traverserFactory = new TraverserFactory($classReflector); diff --git a/tests/ReflectorTest.php b/tests/ReflectorTest.php new file mode 100644 index 00000000..535f3c94 --- /dev/null +++ b/tests/ReflectorTest.php @@ -0,0 +1,220 @@ +, + * 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; + +use Generator; +use PHPUnit\Framework\TestCase; +use Roave\BetterReflection\BetterReflection; +use Roave\BetterReflection\Reflector\ClassReflector; +use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; +use Roave\BetterReflection\SourceLocator\Type\StringSourceLocator; + +/** + * @covers \Humbug\PhpScoper\Reflector + */ +class ReflectorTest extends TestCase +{ + /** + * @dataProvider provideClasses + */ + public function test_it_can_identify_internal_classes(string $code, string $class, bool $expected): void + { + $reflector = $this->createReflector($code); + + $actual = $reflector->isClassInternal($class); + + $this->assertSame($expected, $actual); + } + + /** + * @dataProvider provideFunctions + */ + public function test_it_can_identify_internal_functions(string $code, string $class, bool $expected): void + { + $reflector = $this->createReflector($code); + + $actual = $reflector->isFunctionInternal($class); + + $this->assertSame($expected, $actual); + } + + /** + * @dataProvider provideConstants + */ + public function test_it_can_identify_internal_constants(string $code, string $class, bool $expected): void + { + $reflector = $this->createReflector($code); + + $actual = $reflector->isConstantInternal($class); + + $this->assertSame($expected, $actual); + } + + public function provideClasses(): Generator + { + yield [ + 'astLocator(); + + $sourceLocator = new AggregateSourceLocator([ + new StringSourceLocator($code, $astLocator), + new PhpInternalSourceLocator($astLocator), + ]); + + $classReflector = new ClassReflector($sourceLocator); + + return new Reflector($classReflector); + } +} diff --git a/tests/Scoper/PhpScoperSpecTest.php b/tests/Scoper/PhpScoperSpecTest.php index 3efe10c5..7daaf25b 100644 --- a/tests/Scoper/PhpScoperSpecTest.php +++ b/tests/Scoper/PhpScoperSpecTest.php @@ -23,7 +23,6 @@ use PHPUnit\Framework\TestCase; use Roave\BetterReflection\BetterReflection; use Roave\BetterReflection\Reflector\ClassReflector; -use Roave\BetterReflection\Reflector\FunctionReflector; use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; use Roave\BetterReflection\SourceLocator\Type\StringSourceLocator; @@ -204,10 +203,7 @@ private function createScoper(string $contents): Scoper create_parser(), new FakeScoper(), new TraverserFactory( - new Reflector( - $classReflector, - new FunctionReflector($sourceLocator, $classReflector) - ) + new Reflector($classReflector) ) ); } diff --git a/tests/Scoper/PhpScoperTest.php b/tests/Scoper/PhpScoperTest.php index ad9d46ba..f46dc400 100644 --- a/tests/Scoper/PhpScoperTest.php +++ b/tests/Scoper/PhpScoperTest.php @@ -28,7 +28,6 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use Roave\BetterReflection\Reflector\ClassReflector; -use Roave\BetterReflection\Reflector\FunctionReflector; use function Humbug\PhpScoper\create_fake_patcher; use function Humbug\PhpScoper\create_parser; @@ -89,16 +88,6 @@ class PhpScoperTest extends TestCase */ private $classReflector; - /** - * @var FunctionReflector|ObjectProphecy - */ - private $functionReflectorProphecy; - - /** - * @var FunctionReflector - */ - private $functionReflector; - /** * @inheritdoc */ @@ -119,17 +108,11 @@ public function setUp() $this->classReflectorProphecy = $this->prophesize(ClassReflector::class); $this->classReflector = $this->classReflectorProphecy->reveal(); - $this->functionReflectorProphecy = $this->prophesize(FunctionReflector::class); - $this->functionReflector = $this->functionReflectorProphecy->reveal(); - $this->scoper = new PhpScoper( create_parser(), new FakeScoper(), new TraverserFactory( - new Reflector( - $this->classReflector, - $this->functionReflector - ) + new Reflector($this->classReflector) ) ); }