diff --git a/src/Override.php b/src/Override.php index 8347732..0864b74 100644 --- a/src/Override.php +++ b/src/Override.php @@ -152,7 +152,7 @@ public static function apply( $autoloadCollection->addFile($path); } - // Load the classes that are affected by the fqfc-override converter. + // Load the classes that are affected by the FQFC-override converter. \stream_wrapper_unregister('file'); \stream_wrapper_register('file', FileStreamWrapper::class); foreach ($autoloadCollection->getFilePaths() as $file) { @@ -167,6 +167,7 @@ public static function apply( /** * @param string[]|Closure[] $mappings * @param string $namespace + * * @return array */ private static function buildMappings(array $mappings, string $namespace): array @@ -217,12 +218,11 @@ private static function addNamespaceData(array $directories, array $functionMapp public static function getFunctionMappings(string $filePath): array { $filePath = \realpath($filePath); + $dirPath = dirname($filePath); $mappings = []; - foreach (self::$dirFunctionCallMappings as $dir => $functionMappings) { - if (\substr($filePath, 0, \strlen($dir)) === $dir) { - $mappings = \array_merge($mappings, $functionMappings); - } + if (isset(self::$dirFunctionCallMappings[$dirPath])) { + $mappings = \array_merge($mappings, self::$dirFunctionCallMappings[$dirPath]); } if (isset(self::$fileFunctionCallMappings[$filePath])) { diff --git a/tests/AbstractIntegrationTest.php b/tests/AbstractIntegrationTest.php new file mode 100644 index 0000000..2f367eb --- /dev/null +++ b/tests/AbstractIntegrationTest.php @@ -0,0 +1,35 @@ +overrideApplied) { + $this->overrideApplied = true; + Override::apply(self::$classLoader, $this->getOverrideDeclarations()); + } + } + + abstract protected function getOverrideDeclarations(): array; +} diff --git a/tests/IntegrationClassMapTest.php b/tests/IntegrationClassMapTest.php new file mode 100644 index 0000000..cd5b790 --- /dev/null +++ b/tests/IntegrationClassMapTest.php @@ -0,0 +1,51 @@ + [ + 'cos' => function (float $arg): float { + return \sin($arg); + }, + ], + 'My\\Integration\\TestClassMapOverride\\' => [ + 'cos' => function (float $arg): float { + return $arg * 2; + }, + ] + ]; + } + + public function testCalculator() + { + $calculator = new \My\Integration\TestClassMapOverride\Calculator(); + + // Calls \cos() > Overridden by FQCN-declaration. + $this->assertEquals(\sin(\pi() / 2), $calculator->cos(\pi() / 2)); + } + + public function testGeometry() + { + $geometry = new \My\Integration\TestClassMapOverride\SubNamespace\Geometry(); + + // Calls \cos() > Overridden by FQNS-declaration. + $this->assertEquals(1, $geometry->cos(0.5)); + } + + public function testOtherCalculator() + { + $otherCalculator = new \My\Integration\TestClassMapOverride\OtherCalculator(); + + // Calls \cos() > Overridden by FQNS-declaration. + $this->assertEquals(2, $otherCalculator->cos(1)); + } +} diff --git a/tests/IntegrationClosureTest.php b/tests/IntegrationClosureTest.php new file mode 100644 index 0000000..6a3ddbf --- /dev/null +++ b/tests/IntegrationClosureTest.php @@ -0,0 +1,143 @@ + [ + 'time' => function () { + return 100; + }, + 'rand' => function (int $min, int $max): int { + return $min + $max; + } + ], + \My\Integration\TestClosureOverride\SubSpace\Digital::class => [ + 'rand' => function (int $min, int $max): int { + return 2 * ($min + $max); + } + ], + // Test that Override finds PSR-4 directory belonging to the namespace "My\Integration" and from there the + // directory to the "TestClosureOverride" sub namespace. + 'My\\Integration\\TestClosureOverride\\' => [ + 'rand' => function (int $min, int $max): int { + return 3 * ($min + $max); + } + ], + // Test that Override finds PSR-4 directory belonging to the namespace "My\Integration" and from there the + // directory to the "TestClosureOverride\SubSpace" sub namespace. + 'My\\Integration\\TestClosureOverride\\SubSpace\\' => [ + 'time' => function () { + return 101; + } + ], + \My\Integration\TestClosureOverride\OtherSpace\Other::class => [ + 'time' => function (): int { + return 102; + } + ] + ]; + } + + public function testClock() + { + $clock = new \My\Integration\TestClosureOverride\Clock(); + + // Calls \time() > Overridden by FQCN-declaration. + $this->assertEquals(100, $clock->time()); + + // Calls \time()-alias > Overridden by FQCN-declaration. + $this->assertEquals(100, $clock->timeWithAlias()); + + // Calls \rand() > Overridden by FQCN-declaration. + $this->assertEquals(11, $clock->rand(1, 10)); + } + + public function testSubClock() + { + $subClock = new \My\Integration\TestClosureOverride\SubClock(); + + // Calls \time() > No override. + $this->assertGreaterThanOrEqual(\time(), $subClock->time()); + + // Parent > Calls \time()-alias > Overridden by FQCN-declaration. + $this->assertEquals(100, $subClock->timeWithAlias()); + + // Calls parent > Calls \rand() > Overridden by FQCN-declaration. + $this->assertEquals(9, $subClock->rand(3, 6)); + } + + public function testDigital() + { + $digital = new \My\Integration\TestClosureOverride\SubSpace\Digital(); + + // Calls \time() > Overridden by FQNS-declaration. + $this->assertEquals(101, $digital->time()); + + // Calls \rand() > Overridden by FQCN-declaration. + $this->assertEquals(22, $digital->rand(1, 10)); + } + + public function testSubDigital() + { + $subDigital = new \My\Integration\TestClosureOverride\SubDigital(); + + // Parent > Calls \time() > Overridden by FQNS-declaration. + $this->assertEquals(101, $subDigital->time()); + + // Calls \time() > No override. + $this->assertGreaterThanOrEqual(\time(), $subDigital->subTime()); + + // Calls \rand() > Overridden by FQNS. + $this->assertEquals(33, $subDigital->rand(1, 10)); + } + + public function testSubSpaceClock() + { + $subSpaceClock = new \My\Integration\TestClosureOverride\SubSpace\SubSpaceClock(); + + // Calls \time() > Overridden by FQNS. + $this->assertEquals(101, $subSpaceClock->time()); + + // Parent > Calls \time()-alias > Overridden by FQCN-declaration. + $this->assertEquals(100, $subSpaceClock->timeWithAlias()); + + // Calls \rand() > No override. + $rand = $subSpaceClock->rand(1, 10); + $this->assertGreaterThanOrEqual(1, $rand); + $this->assertLessThanOrEqual(10, $rand); + } + + public function testOther() + { + $other = new \My\Integration\TestClosureOverride\OtherSpace\Other(); + + // Calls \time() > Overridden by FQCN-declaration. + $this->assertEquals(102, $other->time()); + + // Calls \rand() > No override. + $rand = $other->rand(1, 10); + $this->assertGreaterThanOrEqual(1, $rand); + $this->assertLessThanOrEqual(10, $rand); + } + + public function testSpace() + { + $space = new \My\Integration\TestClosureOverride\OtherSpace\Space(); + + // Calls \time() > No override. + $this->assertGreaterThanOrEqual(\time(), $space->time()); + + // Calls time() which is a local function in the namespace. + $this->assertEquals(105, $space->timeLocal()); + } +} diff --git a/tests/IntegrationCustomNamespaceTest.php b/tests/IntegrationCustomNamespaceTest.php new file mode 100644 index 0000000..aab1517 --- /dev/null +++ b/tests/IntegrationCustomNamespaceTest.php @@ -0,0 +1,34 @@ + [ + 'md5' => 'PHPCustomAutoloadOverride' + ] + ]; + } + + public function testHash() + { + $hash = new \My\Integration\TestCustomNamespaceOverride\Hash(); + + // Calls \md5() > Overridden by FQCN-declaration. + $GLOBALS['md5_return'] = '---'; + $this->assertEquals('---', $hash->hash('1')); + } +} diff --git a/tests/IntegrationNamespaceTest.php b/tests/IntegrationNamespaceTest.php new file mode 100644 index 0000000..4f16c3a --- /dev/null +++ b/tests/IntegrationNamespaceTest.php @@ -0,0 +1,61 @@ + [ + 'time' + ], + 'My\\Integration\\TestNamespaceOverride\\' => [ + 'substr' + ], + ]; + } + + public function testEarth() + { + $earth = new \My\Integration\TestNamespaceOverride\Earth(); + + // Calls substr() which is a local function in the namespace. + $this->assertEquals('GFE', $earth->substrLocal()); + + // Calls \substr() > Overridden by FQNS-declaration. + $GLOBALS['substr_return'] = 'XYZ'; + $this->assertEquals('XYZ', $earth->substrGlobal()); + + // 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. + $GLOBALS['time_return'] = 1; + $this->assertEquals(1, $moon->time()); + + // Calls \time()-alias > Overridden by FQCN. + $GLOBALS['time_return'] = 2; + $this->assertEquals(2, $moon->timeUseAlias()); + + // Calls \substr() > Overridden by FQNS-declaration. + $GLOBALS['substr_return'] = 'ZZZ'; + $this->assertEquals('ZZZ', $moon->substr('AAA', 0, 2)); + } +} diff --git a/tests/IntegrationPsr4MultiDirTest.php b/tests/IntegrationPsr4MultiDirTest.php new file mode 100644 index 0000000..8965d76 --- /dev/null +++ b/tests/IntegrationPsr4MultiDirTest.php @@ -0,0 +1,43 @@ + [ + 'str_repeat' => function ($str, $multiplier) { + return \str_repeat($str, 2 * $multiplier); + } + ], + 'AdrianSuter\\Autoload\\Override\\SubSpace\\' => [ + 'str_repeat' => function ($input, $multiplier) { + return ':'; + } + ], + ]; + } + + public function testScience() + { + $science = new \AdrianSuter\Autoload\Override\Science(); + + // Calls \str_repeat() > Overridden by FQCN-declaration. + $this->assertEquals('xxxx', $science->crosses(2)); + } + + public function testSpeech() + { + $speech = new \AdrianSuter\Autoload\Override\SubSpace\Speech(); + + // Calls \str_repeat() > Overridden by FQNS-declaration. + $this->assertEquals(':', $speech->whisper(2)); + } +} diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php deleted file mode 100644 index 6b9634d..0000000 --- a/tests/IntegrationTest.php +++ /dev/null @@ -1,178 +0,0 @@ - [ - 'time' - ], - 'My\\Integration\\TestNamespaceOverride\\' => [ - 'substr' - ], - ]); - - require_once __DIR__ . '/assets/PHPAutoloadOverride.php'; - - $moon = new \My\Integration\TestNamespaceOverride\Moon(); - $earth = new \My\Integration\TestNamespaceOverride\Earth(); - - $this->setName(__FUNCTION__ . ' (FQCN defined override to default NS)'); - $GLOBALS['time_return'] = 1; - $this->assertEquals(1, $moon->now()); - - $this->setName(__FUNCTION__ . ' (FQCN defined override to default NS, function call uses an alias)'); - $GLOBALS['time_return'] = 2; - $this->assertEquals(2, $moon->nowUseAlias()); - - $this->setName(__FUNCTION__ . ' (FQNS defined override to default NS, but function call uses a local function)'); - $this->assertEquals('GFE', $earth->substrLocal()); - - $this->setName(__FUNCTION__ . ' (FQNS defined override to default NS)'); - $GLOBALS['substr_return'] = 'XYZ'; - $this->assertEquals('XYZ', $earth->substrGlobal()); - - $this->setName(__FUNCTION__ . ' (No override)'); - $GLOBALS['time_return'] = 3; - $this->assertGreaterThanOrEqual(\time(), $earth->now()); - } - - public function testCustomNamespaceOverride() - { - Override::apply(self::$classLoader, [ - \My\Integration\TestCustomNamespaceOverride\Hash::class => [ - 'md5' => 'PHPCustomAutoloadOverride' - ] - ]); - - require_once __DIR__ . '/assets/PHPCustomAutoloadOverride.php'; - - $hash = new \My\Integration\TestCustomNamespaceOverride\Hash(); - $GLOBALS['md5_return'] = '---'; - $this->assertEquals('---', $hash->hash('1')); - } - - public function testClosureOverride() - { - $that = $this; - - Override::apply(self::$classLoader, [ - // Test that the class loader can find the file to that class. - \My\Integration\TestClosureOverride\Clock::class => [ - // [1] - 'time' => function () { - return 99; - } - ], - // Test that the class loader can find the file to that class. - \My\Integration\TestClosureOverride\BigBen::class => [ - // [2] - 'date' => function ($format, $timestamp) use ($that) { - $that->assertEquals('h', $format); - $that->assertNotEquals(99, $timestamp); - $that->assertNotEquals(101, $timestamp); - - return '6'; - } - ], - // Test that Override finds PSR-4 directory belonging to the namespace "My\Integration" and from there the - // directory to the "TestClosureOverride" sub namespace. - 'My\\Integration\\TestClosureOverride\\' => [ - // [3] - 'date' => function ($format, $timestamp) { - return '_' . $format . '_' . $timestamp . '_'; - } - ], - // Test that Override finds PSR-4 directory belonging to the namespace "My\Integration" and from there the - // directory to the "TestClosureOverride\SubSpace" sub namespace. - 'My\\Integration\\TestClosureOverride\\SubSpace\\' => [ - // [4] - 'time' => function () { - return 101; - } - ] - ]); - - // Override \time() [1] and \date() [3]. - $clock = new \My\Integration\TestClosureOverride\Clock(); - $this->assertEquals(99, $clock->now()); - $this->assertEquals('_H_99_', $clock->hour()); - - // Override \date() [2, not 3, not 4]. - $bigBen = new \My\Integration\TestClosureOverride\BigBen(); - $this->assertEquals('******', $bigBen->hour()); - - // Override \date() [3] and \time() [4]. - $digital = new \My\Integration\TestClosureOverride\SubSpace\Digital(); - $this->assertEquals('_i_101_', $digital->minute()); - - $this->setName(__FUNCTION__ . ' (No override)'); - $mercury = new \My\Integration\TestClosureOverride\Solar\Mercury(); - $this->assertEquals(\date('d.m.Y', \time()), $mercury->now('d.m.Y')); - } - - public function testDouble() - { - Override::apply(self::$classLoader, [ - \AdrianSuter\Autoload\Override\Science::class => [ - 'str_repeat' => function ($str, $multiplier) { - return \str_repeat($str, 2 * $multiplier); - } - ], - 'AdrianSuter\\Autoload\\Override\\SubSpace\\' => [ - 'str_repeat' => function ($input, $multiplier) { - return ':'; - } - ], - ]); - - $science = new \AdrianSuter\Autoload\Override\Science(); - $this->assertEquals('xxxx', $science->crosses(2)); - - $speech = new \AdrianSuter\Autoload\Override\SubSpace\Speech(); - $this->assertEquals(':', $speech->whisper(2)); - } - - public function testA() - { - Override::apply(self::$classLoader, [ - \My\Integration\TestClassMapOverride\Calculator::class => [ - 'cos' => function (float $arg): float { - return \sin($arg); - }, - ], - 'My\\Integration\\TestClassMapOverride\\' => [ - 'cos' => function (float $arg): float { - return $arg * 2; - }, - ] - ]); - - $calculator = new \My\Integration\TestClassMapOverride\Calculator(); - $this->assertEquals(\sin(\pi() / 2), $calculator->cos(\pi() / 2)); - - $geometry = new \My\Integration\TestClassMapOverride\SubNamespace\Geometry(); - $this->assertEquals(1, $geometry->cos(0.5)); - - $otherCalculator = new \My\Integration\TestClassMapOverride\OtherCalculator(); - $this->assertEquals(2, $otherCalculator->cos(1)); - } -} diff --git a/tests/integration/src-override/Science.php b/tests/integration/src-override/Science.php index ad2b135..ebdb030 100644 --- a/tests/integration/src-override/Science.php +++ b/tests/integration/src-override/Science.php @@ -9,6 +9,12 @@ namespace AdrianSuter\Autoload\Override; +/** + * Overrides declared for + * - \str_repeat() : FQCN + * + * @package AdrianSuter\Autoload\Override + */ class Science { public function crosses($amount): string diff --git a/tests/integration/src-override/SubSpace/Speech.php b/tests/integration/src-override/SubSpace/Speech.php index c7c7495..92234d1 100644 --- a/tests/integration/src-override/SubSpace/Speech.php +++ b/tests/integration/src-override/SubSpace/Speech.php @@ -9,6 +9,12 @@ namespace AdrianSuter\Autoload\Override\SubSpace; +/** + * Overrides declared for + * - \str_repeat() : FQNS + * + * @package AdrianSuter\Autoload\Override\SubSpace + */ class Speech { public function whisper(int $amount): string diff --git a/tests/integration/src/TestClassMapOverride/Calculator.php b/tests/integration/src/TestClassMapOverride/Calculator.php index 65acb4b..1904a5f 100644 --- a/tests/integration/src/TestClassMapOverride/Calculator.php +++ b/tests/integration/src/TestClassMapOverride/Calculator.php @@ -9,6 +9,12 @@ namespace My\Integration\TestClassMapOverride; +/** + * Overrides declared for + * - \cos() : FQCN + * + * @package My\Integration\TestClassMapOverride + */ class Calculator { public function cos(float $arg): float diff --git a/tests/integration/src/TestClassMapOverride/OtherCalculator.php b/tests/integration/src/TestClassMapOverride/OtherCalculator.php index 04ccd42..c5ad3d1 100644 --- a/tests/integration/src/TestClassMapOverride/OtherCalculator.php +++ b/tests/integration/src/TestClassMapOverride/OtherCalculator.php @@ -9,6 +9,12 @@ namespace My\Integration\TestClassMapOverride; +/** + * Overrides declared for + * - \cos() : FQNS + * + * @package My\Integration\TestClassMapOverride + */ class OtherCalculator { public function cos(float $arg): float diff --git a/tests/integration/src/TestClassMapOverride/SubNamespace/Geometry.php b/tests/integration/src/TestClassMapOverride/SubNamespace/Geometry.php index 9ad8840..f053a46 100644 --- a/tests/integration/src/TestClassMapOverride/SubNamespace/Geometry.php +++ b/tests/integration/src/TestClassMapOverride/SubNamespace/Geometry.php @@ -9,6 +9,12 @@ namespace My\Integration\TestClassMapOverride\SubNamespace; +/** + * Overrides declared for + * - \cos() : FQNS + * + * @package My\Integration\TestClassMapOverride\SubNamespace + */ class Geometry { public function cos(float $arg): float diff --git a/tests/integration/src/TestClosureOverride/Clock.php b/tests/integration/src/TestClosureOverride/Clock.php index 6153cfb..cc05dba 100644 --- a/tests/integration/src/TestClosureOverride/Clock.php +++ b/tests/integration/src/TestClosureOverride/Clock.php @@ -9,15 +9,29 @@ namespace My\Integration\TestClosureOverride; +use function time as timeAlias; + +/** + * Overrides declared for + * - \time() : FQCN + * - \rand() : FQCN + * + * @package My\Integration\TestClosureOverride + */ class Clock { - public function now(): int + public function time(): int { return \time(); } - public function hour(): string + public function timeWithAlias(): int + { + return timeAlias(); + } + + public function rand(int $min, int $max): int { - return \date('H', \time()); + return \rand($min, $max); } } diff --git a/tests/integration/src/TestClosureOverride/OtherSpace/Other.php b/tests/integration/src/TestClosureOverride/OtherSpace/Other.php new file mode 100644 index 0000000..627ddb2 --- /dev/null +++ b/tests/integration/src/TestClosureOverride/OtherSpace/Other.php @@ -0,0 +1,29 @@ +