Repository: laravel/framework (php)

Issue:
[5.2] Implicit route model binding a binded interface
Hello, I try to do a implicit route model binding on a interface that is binded to my User model but it wont work. I created a thread on Laracasts about this, so you can see the example over there. https://laracasts.com/discuss/channels/laravel/implicit-route-model-bind-a-contract

EDIT: Adding the example here as well

```php
// routes.php
$router->get('/implicit/{id}', function(App\Contracts\User $id){
    dd($id);
});
```
```php
// AppServiceProvider .php
class AppServiceProvider extends ServiceProvider
{
    /**
     * Hold the bidnings
     *
     * @var array
     */
    protected $bindings = [
        'user' => [\App\Contracts\User::class => \App\User::class],
    ];

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register
     *
     * @return void
     */
    public function register()
    {
        if (! isset($this->bindings)) return;

        foreach ($this->bindings as $alias => $binding) {
            list($abstract, $concrete) = [key($binding), current($binding)];

            $this->app->bind([$alias => $abstract], $concrete);
        }
    }
}
```
```php
// User.php
class User extends Authenticatable implements Contracts\User
{
    protected $table = 'players';
}
```
````php
// Contracts/User.php
interface User
{

}
```

Is there a way to accomplish this? Thanks :)

Pull request:
[8.x] Improves `Support\Reflector` to support checking interfaces
Since `ReflectionClass` already may represent an interface, we may check for interface existence as part of checking parameter subclass.

`isParameterSubclassOf` is used in two places for model route binding:

- `Routing\RouteSignatureParameters`
- `Broadcasting\Broadcasters\Broadcaster`

This improvement helps with implicit binding when the parameter is an interface served from the container
```php

use Illuminate\Contracts\Routing\UrlRoutable;

interface MyUrlRoutableModel extends UrlRoutable {
 //
}

Route::get('/{model}', function (MyUrlRoutableModel $model) {
   // $model binds correctly
});
```
Fixes #11791

Patch:
diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php
index 7a75f5f090..b2075757d7 100644
--- a/src/Illuminate/Support/Reflector.php
+++ b/src/Illuminate/Support/Reflector.php
@@ -136,7 +136,8 @@ public static function isParameterSubclassOf($parameter, $className)
         $paramClassName = static::getParameterClassName($parameter);
 
         return $paramClassName
-            && class_exists($paramClassName)
+            && (class_exists($paramClassName) ||
+                interface_exists($paramClassName))
             && (new ReflectionClass($paramClassName))->isSubclassOf($className);
     }
 }


Test patch:
diff --git a/tests/Support/SupportReflectorTest.php b/tests/Support/SupportReflectorTest.php
index c7aaffe51c..9e4dfda394 100644
--- a/tests/Support/SupportReflectorTest.php
+++ b/tests/Support/SupportReflectorTest.php
@@ -48,6 +48,13 @@ public function testParentClassName()
         $this->assertSame(A::class, Reflector::getParameterClassName($method->getParameters()[0]));
     }
 
+    public function testParameterSubclassOfInterface()
+    {
+        $method = (new ReflectionClass(TestClassWithInterfaceSubclassParameter::class))->getMethod('f');
+
+        $this->assertTrue(Reflector::isParameterSubclassOf($method->getParameters()[0], IA::class));
+    }
+
     /**
      * @requires PHP >= 8
      */
@@ -95,8 +102,7 @@ public function f(A|Model $x)
     {
         //
     }
-}'
-    );
+}');
 }
 
 class TestClassWithCall
@@ -114,3 +120,19 @@ public static function __callStatic($method, $parameters)
         //
     }
 }
+
+interface IA
+{
+}
+
+interface IB extends IA
+{
+}
+
+class TestClassWithInterfaceSubclassParameter
+{
+    public function f(IB $x)
+    {
+        //
+    }
+}