Skip to content

Commit fa89963

Browse files
authored
fix: Add reproducer for conflicting humbug_phpscoper_expose_class declarations #831
This is the failing test case for #811
1 parent d9a5f45 commit fa89963

File tree

13 files changed

+254
-5
lines changed

13 files changed

+254
-5
lines changed

.github/workflows/e2e-tests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ jobs:
8484
- 'e2e_034'
8585
- 'e2e_035'
8686
- 'e2e_036'
87+
- 'e2e_037'
8788
php:
8889
- '8.1'
8990
- '8.2'

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
/fixtures/set030/composer.lock
2222
/fixtures/set030/expected-output
2323
/fixtures/set031-extension-symbol/expected-output
24+
/fixtures/set037-double-scoping/bin/scoped-greet.phar
2425
/infection-log.txt
2526
/phpcs.xml
2627
/vendor-bin/*/bin/

.makefile/e2e.file

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ e2e_005: $(PHP_SCOPER_PHAR_BIN) fixtures/set005/vendor
2222

2323
.PHONY: e2e_011
2424
e2e_011: # Runs end-to-end tests for the fixture set 011 — Codebase with exposed symbols
25-
e2e_011: $(PHP_SCOPER_PHAR_BIN) fixtures/set011/vendor
26-
$(BOX) compile --no-parallel --working-dir fixtures/set011
25+
e2e_011: $(PHP_SCOPER_PHAR_BIN) build/set011/bin/greet.phar
2726
cp -R fixtures/set011/tests/ build/set011/tests/
2827

2928
php build/set011/bin/greet.phar > build/set011/output
@@ -334,8 +333,6 @@ e2e_036: $(PHP_SCOPER_PHAR_BIN)
334333
# Identical to set004 but without Box
335334
rm fixtures/set036/composer.lock || true
336335
rm -rf fixtures/set036/vendor || true
337-
rm -rf build/set036 || true
338-
mkdir -p build/set036
339336

340337
composer --working-dir=fixtures/set036 dump-autoload
341338

@@ -351,11 +348,35 @@ e2e_036: $(PHP_SCOPER_PHAR_BIN)
351348
php build/set036/bin/greet.php > build/set036/output
352349
diff fixtures/set036/expected-output build/set036/output
353350

351+
.PHONY: e2e_037
352+
e2e_037: # Runs end-to-end tests for the fixture set e2e_037 — Codebase using scoped code
353+
e2e_037: $(PHP_SCOPER_PHAR_BIN) build/set011/bin/greet.phar
354+
rm fixtures/set037-double-scoping/composer.lock || true
355+
rm -rf fixtures/set037-double-scoping/vendor || true
356+
357+
composer --working-dir=fixtures/set037-double-scoping dump-autoload
358+
cp build/set011/bin/greet.phar fixtures/set037-double-scoping/bin/scoped-greet.phar
359+
360+
$(PHP_SCOPER_PHAR) add-prefix . \
361+
--working-dir=fixtures/set037-double-scoping \
362+
--output-dir=../../build/set037 \
363+
--force \
364+
--no-interaction \
365+
--stop-on-failure
366+
cp -R fixtures/set037-double-scoping/tests/ build/set037/tests/
367+
composer --working-dir=build/set037 dump-autoload
368+
369+
php build/set037/bin/greet.php > build/set037/output
370+
diff fixtures/set037-double-scoping/expected-output build/set037/output
371+
354372

355373
#
356374
# Rules from files
357375
#---------------------------------------------------------------------------
358376

377+
build/set011/bin/greet.phar: $(PHP_SCOPER_PHAR_BIN) fixtures/set011/vendor
378+
$(BOX) compile --no-parallel --working-dir fixtures/set011
379+
touch -c $@
359380

360381
fixtures/set005/vendor: fixtures/set005/composer.lock
361382
composer --working-dir=fixtures/set005 install

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ infection: $(COVERAGE_XML) vendor
147147
include .makefile/e2e.file
148148
.PHONY: e2e
149149
e2e: ## Runs end-to-end tests
150-
e2e: e2e_004 e2e_005 e2e_011 e2e_013 e2e_014 e2e_015 e2e_016 e2e_017 e2e_018 e2e_019 e2e_020 e2e_024 e2e_025 e2e_027 e2e_028 e2e_029 e2e_030 e2e_031 e2e_032 e2e_033 e2e_034 e2e_035 e2e_036
150+
e2e: e2e_004 e2e_005 e2e_011 e2e_013 e2e_014 e2e_015 e2e_016 e2e_017 e2e_018 e2e_019 e2e_020 e2e_024 e2e_025 e2e_027 e2e_028 e2e_029 e2e_030 e2e_031 e2e_032 e2e_033 e2e_034 e2e_035 e2e_036 e2e_037
151151

152152
.PHONY: blackfire
153153
blackfire: ## Runs Blackfire profiling
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
if (file_exists(__DIR__.'/../vendor/scoper-autoload.php')) {
6+
require_once __DIR__.'/../vendor/scoper-autoload.php';
7+
require __DIR__.'/scoped-greet.phar';
8+
echo "It works!".PHP_EOL;
9+
exit(0);
10+
} else {
11+
require_once __DIR__.'/../vendor/autoload.php';
12+
}
13+
14+
use Set011\DirectionaryLocator;
15+
use Set011\Greeter;
16+
use Set011\Dictionary;
17+
18+
$dir = Phar::running(false);
19+
20+
if ('' === $dir) {
21+
// Running outside of a PHAR
22+
$dir = __DIR__.DIRECTORY_SEPARATOR.'bin';
23+
}
24+
25+
$testDir = dirname($dir).'/../tests';
26+
27+
$dictionaries = DirectionaryLocator::locateDictionaries($testDir);
28+
29+
$words = array_reduce(
30+
$dictionaries,
31+
function (array $words, Dictionary $dictionary): array {
32+
$words = array_merge($words, $dictionary->provideWords());
33+
34+
return $words;
35+
},
36+
[]
37+
);
38+
39+
$greeter = new Greeter($words);
40+
41+
foreach ($greeter->greet() as $greeting) {
42+
echo $greeting.PHP_EOL;
43+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"bin": [
3+
"bin/greet.php"
4+
],
5+
"autoload": {
6+
"classmap": ["polyfill"],
7+
"psr-4": {
8+
"Set011\\": "src/"
9+
}
10+
},
11+
"autoload-dev": {
12+
"psr-4": {
13+
"Set011\\": "tests/"
14+
}
15+
}
16+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
It works!
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/**
4+
* Let's polyfill the iterator interface.
5+
*
6+
* We use it as example to alias an interface into the root namespace.
7+
* It will never get loaded for real as it exists on all PHP versions.
8+
*
9+
* @link https://php.net/manual/en/class.iterator.php.
10+
* @link https://github.com/humbug/php-scoper/issues/403
11+
*/
12+
interface Iterator extends Traversable {
13+
public function current();
14+
public function next(): void;
15+
public function key();
16+
public function valid(): bool;
17+
public function rewind(): void;
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the humbug/php-scoper package.
7+
*
8+
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
9+
* Pádraic Brady <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
return [
16+
'expose-classes' => [
17+
\Set011\Dictionary::class,
18+
],
19+
];
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Set011;
6+
7+
interface Dictionary
8+
{
9+
/**
10+
* @return string[]
11+
*/
12+
public function provideWords(): array;
13+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Set011;
6+
7+
use ReflectionClass;
8+
9+
final class DirectionaryLocator
10+
{
11+
/**
12+
* @param string $dir
13+
*
14+
* @return Dictionary[]
15+
*/
16+
public static function locateDictionaries(string $dir): array
17+
{
18+
$dictionaryFiles = array_values(
19+
array_filter(
20+
array_map(
21+
function (string $filePath) use ($dir) {
22+
return realpath($dir.DIRECTORY_SEPARATOR.$filePath);
23+
},
24+
array_filter(
25+
scandir($dir),
26+
function (string $file): bool {
27+
return 1 === preg_match('/.*Dictionary\.php$/', $file);
28+
}
29+
)
30+
),
31+
function ($filePath): bool {
32+
return false !== $filePath;
33+
34+
}
35+
)
36+
);
37+
38+
$classes = get_declared_classes();
39+
40+
foreach ($dictionaryFiles as $dictionaryFile) {
41+
include $dictionaryFile;
42+
}
43+
44+
$newClasses = array_diff(get_declared_classes(), $classes);
45+
46+
return array_reduce(
47+
$newClasses,
48+
function (array $dictionaries, string $className): array {
49+
$class = new ReflectionClass($className);
50+
51+
if (false === $class->isAbstract() && $class->implementsInterface(Dictionary::class)) {
52+
$dictionaries[] = $class->newInstanceWithoutConstructor();
53+
}
54+
55+
return $dictionaries;
56+
},
57+
[]
58+
);
59+
}
60+
61+
private function __construct()
62+
{
63+
64+
}
65+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Set011;
6+
7+
use Generator;
8+
9+
class Greeter
10+
{
11+
private $words;
12+
13+
/**
14+
* @param string[] $words
15+
*/
16+
public function __construct(array $words)
17+
{
18+
$this->words = $words;
19+
}
20+
21+
/**
22+
* @return Generator|string
23+
*/
24+
public function greet()
25+
{
26+
foreach ($this->words as $word) {
27+
yield $word.' world!';
28+
}
29+
}
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Set011;
6+
7+
final class SalutationDictionary implements Dictionary
8+
{
9+
/**
10+
* @inheritdoc
11+
*/
12+
public function provideWords(): array
13+
{
14+
return [
15+
'Hello',
16+
'Hi',
17+
'Salut',
18+
'Bonjour',
19+
];
20+
}
21+
}

0 commit comments

Comments
 (0)