Repository: humbug/php-scoper (php)

Issue:
scoper-autoload.php using class_exists for scoped interfaces
## Bug report

| Question    | Answer
| ------------| ---------------
| Box version | 0.13.3
| PHP version | 7.2.29
| Platform with version | MacOS

scoped output of `vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php`

```php
<?php

namespace MyNamespace;

interface SessionUpdateTimestampHandlerInterface
{
    /**
     * Checks if a session identifier already exists or not.
     *
     * @param string $key
     *
     * @return bool
     */
    public function validateId($key);
    /**
     * Updates the timestamp of a session when its data didn't change.
     *
     * @param string $key
     * @param string $val
     *
     * @return bool
     */
    public function updateTimestamp($key, $val);
}
\class_alias('MyNamespace\\SessionUpdateTimestampHandlerInterface', 'SessionUpdateTimestampHandlerInterface', \false);
```

`scoper-autoload.php` contains the following snippet:
```php
if (!class_exists('SessionUpdateTimestampHandlerInterface', false)) {
    class_exists('MyNamespace\SessionUpdateTimestampHandlerInterface');
}
```
The above results in an error:
```
Cannot declare interface SessionUpdateTimestampHandlerInterface, because the name is already in use

vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php:25
class_alias()
vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php:25
Composer\A\includeFile()
vendor/composer/ClassLoader.php:322
Composer\A\ClassLoader->loadClass()
Unknown location
spl_autoload_call()
Unknown location
class_exists('MyNamespace\SessionUpdateTimestampHandlerInterface')
vendor/scoper-autoload.php:25
```

If I change the `scoper-autoload.php` snippet above to the following instead, the error is gone:
```php
if (!interface_exists('SessionUpdateTimestampHandlerInterface', false)) {
    interface_exists('MyNamespace\SessionUpdateTimestampHandlerInterface');
}
```

Is there something I am missing to make sure `scoper-autoload.php` correctly uses `interface_exists` for Interfaces?

Pull request description:
Also check for interfaces and traits in existence checks
Only performing `class_exists()` is not sufficient as interfaces and
traits are different kind of autoloadable things.
Therefore we have to test that none of these exist prior trying to
autoload an alias them.

Fixes #403

Patch:
diff --git a/fixtures/set011/composer.json b/fixtures/set011/composer.json
index e50385e..f1575c8 100644
--- a/fixtures/set011/composer.json
+++ b/fixtures/set011/composer.json
@@ -3,6 +3,7 @@
         "bin/greet.php"
     ],
     "autoload": {
+        "classmap": ["polyfill"],
         "psr-4": {
             "Set011\\": "src/"
         }
diff --git a/fixtures/set011/polyfill/Iterator.php b/fixtures/set011/polyfill/Iterator.php
new file mode 100644
index 0000000..2cf5cc7
--- /dev/null
+++ b/fixtures/set011/polyfill/Iterator.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Let's polyfill the iterator interface.
+ *
+ * We use it as example to alias an interface into the root namespace.
+ * It will never get loaded for real as it exists on all PHP versions.
+ *
+ * @link https://php.net/manual/en/class.iterator.php.
+ * @link https://github.com/humbug/php-scoper/issues/403
+ */
+interface Iterator extends Traversable {
+    public function current();
+    public function next(): void;
+    public function key();
+    public function valid(): bool;
+    public function rewind(): void;
+}
diff --git a/src/Autoload/ScoperAutoloadGenerator.php b/src/Autoload/ScoperAutoloadGenerator.php
index 8545740..24565cb 100644
--- a/src/Autoload/ScoperAutoloadGenerator.php
+++ b/src/Autoload/ScoperAutoloadGenerator.php
@@ -113,8 +113,8 @@ PHP;
 
                 return sprintf(
                     <<<'PHP'
-if (!class_exists('%s', false)) {
-    class_exists('%s');
+if (!class_exists('%1$s', false) && !interface_exists('%1$s', false) && !trait_exists('%1$s', false)) {
+    spl_autoload_call('%2$s');
 }
 PHP
                     ,


Test patch:
diff --git a/tests/Autoload/ScoperAutoloadGeneratorTest.php b/tests/Autoload/ScoperAutoloadGeneratorTest.php
index c501bdf..79cfee7 100644
--- a/tests/Autoload/ScoperAutoloadGeneratorTest.php
+++ b/tests/Autoload/ScoperAutoloadGeneratorTest.php
@@ -187,8 +187,8 @@ $loader = require_once __DIR__.'/autoload.php';
 
 // Aliases for the whitelisted classes. For more information see:
 // https://github.com/humbug/php-scoper/blob/master/README.md#class-whitelisting
-if (!class_exists('A\Foo', false)) {
-    class_exists('Humbug\A\Foo');
+if (!class_exists('A\Foo', false) && !interface_exists('A\Foo', false) && !trait_exists('A\Foo', false)) {
+    spl_autoload_call('Humbug\A\Foo');
 }
 
 return $loader;
@@ -221,11 +221,11 @@ $loader = require_once __DIR__.'/autoload.php';
 
 // Aliases for the whitelisted classes. For more information see:
 // https://github.com/humbug/php-scoper/blob/master/README.md#class-whitelisting
-if (!class_exists('Foo', false)) {
-    class_exists('Humbug\Foo');
+if (!class_exists('Foo', false) && !interface_exists('Foo', false) && !trait_exists('Foo', false)) {
+    spl_autoload_call('Humbug\Foo');
 }
-if (!class_exists('Bar', false)) {
-    class_exists('Humbug\Bar');
+if (!class_exists('Bar', false) && !interface_exists('Bar', false) && !trait_exists('Bar', false)) {
+    spl_autoload_call('Humbug\Bar');
 }
 
 return $loader;
@@ -281,8 +281,8 @@ namespace {
 // Aliases for the whitelisted classes. For more information see:
 // https://github.com/humbug/php-scoper/blob/master/README.md#class-whitelisting
 namespace {
-    if (!class_exists('A\Foo', false)) {
-        class_exists('Humbug\A\Foo');
+    if (!class_exists('A\Foo', false) && !interface_exists('A\Foo', false) && !trait_exists('A\Foo', false)) {
+        spl_autoload_call('Humbug\A\Foo');
     }
 }