diff --git a/composer.json b/composer.json index 50f42f5..0a4357f 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "monolog/monolog": "~1.6", "nesbot/carbon": "~1.0", "patchwork/utf8": "~1.1", - "phpseclib/phpseclib": "0.3.*", + "phpseclib/phpseclib": "~0.3.10||~2.0", "predis/predis": "^1.1", "stack/builder": "~1.0", "swiftmailer/swiftmailer": "~5.1", @@ -29,11 +29,11 @@ "symfony/debug": "2.7.*", "symfony/dom-crawler": "2.7.*", "symfony/finder": "2.7.*", - "symfony/http-foundation": "2.7.*", - "symfony/http-kernel": "2.7.*", + "symfony/http-foundation": "2.8.52", + "symfony/http-kernel": "2.8.52", "symfony/process": "2.7.*", "symfony/routing": "2.7.*", - "symfony/security-core": "2.7.*", + "symfony/security-core": "2.8.*", "symfony/translation": "2.7.*" }, "replace": { diff --git a/src/Illuminate/Container/Container.php b/src/Illuminate/Container/Container.php index 84b606e..c9aea1f 100644 --- a/src/Illuminate/Container/Container.php +++ b/src/Illuminate/Container/Container.php @@ -542,7 +542,7 @@ public function build($concrete, $parameters = array()) /** * Resolve all of the dependencies from the ReflectionParameters. * - * @param array $parameters + * @param \ReflectionParameter[] $parameters * @param array $primitives * @return array */ @@ -552,7 +552,9 @@ protected function getDependencies($parameters, array $primitives = array()) foreach ($parameters as $parameter) { - $dependency = $parameter->getClass(); + $dependency = $parameter->getType() && ! $parameter->getType()->isBuiltin() + ? new ReflectionClass($parameter->getType()->getName()) + : null; // If the class is null, it means the dependency is a string or some other // primitive type which we can not resolve since it is not a class and @@ -606,7 +608,7 @@ protected function resolveClass(ReflectionParameter $parameter) { try { - return $this->make($parameter->getClass()->name); + return $this->make($parameter->getType()->getName()); } // If we can not resolve the class instance, we will check to see if the value diff --git a/src/Illuminate/Routing/Controller.php b/src/Illuminate/Routing/Controller.php index 62fe093..3a49c26 100644 --- a/src/Illuminate/Routing/Controller.php +++ b/src/Illuminate/Routing/Controller.php @@ -228,7 +228,7 @@ public function callAction($method, $parameters) { $this->setupLayout(); - $response = call_user_func_array(array($this, $method), $parameters); + $response = call_user_func_array(array($this, $method), array_values($parameters)); // If no response is returned from the controller action and a layout is being // used we will assume we want to just return the layout view as any nested diff --git a/src/Illuminate/Routing/Route.php b/src/Illuminate/Routing/Route.php index 093014f..d078e74 100644 --- a/src/Illuminate/Routing/Route.php +++ b/src/Illuminate/Routing/Route.php @@ -1,5 +1,6 @@ parameters(), function($p) { return isset($p); }); + $callable = $this->action['uses']; - return call_user_func_array($this->action['uses'], $parameters); + return $callable(...array_values($this->resolveMethodDependencies( + $this->parametersWithoutNulls(), new \ReflectionFunction($callable) + ))); } /** @@ -811,4 +823,16 @@ public function getCompiled() return $this->compiled; } + /** + * Set the container instance on the route. + * + * @param \Illuminate\Container\Container $container + * @return $this + */ + public function setContainer(Container $container) + { + $this->container = $container; + + return $this; + } } diff --git a/src/Illuminate/Routing/RouteDependencyResolverTrait.php b/src/Illuminate/Routing/RouteDependencyResolverTrait.php new file mode 100644 index 0000000..a288945 --- /dev/null +++ b/src/Illuminate/Routing/RouteDependencyResolverTrait.php @@ -0,0 +1,112 @@ +resolveMethodDependencies( + $parameters, new ReflectionMethod($instance, $method) + ); + } + + /** + * Resolve the given method's type-hinted dependencies. + * + * @param array $parameters + * @param \ReflectionFunctionAbstract $reflector + * @return array + */ + public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector) + { + $instanceCount = 0; + + $values = array_values($parameters); + + $skippableValue = new stdClass; + + foreach ($reflector->getParameters() as $key => $parameter) { + $instance = $this->transformDependency($parameter, $parameters, $skippableValue); + + if ($instance !== $skippableValue) { + $instanceCount++; + + $this->spliceIntoParameters($parameters, $key, $instance); + } elseif (! isset($values[$key - $instanceCount]) && + $parameter->isDefaultValueAvailable()) { + $this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue()); + } + } + + return $parameters; + } + + /** + * Attempt to transform the given parameter into a class instance. + * + * @param \ReflectionParameter $parameter + * @param array $parameters + * @param object $skippableValue + * @return mixed + */ + protected function transformDependency(ReflectionParameter $parameter, $parameters, $skippableValue) + { + $className = Reflector::getParameterClassName($parameter); + + // If the parameter has a type-hinted class, we will check to see if it is already in + // the list of parameters. If it is we will just skip it as it is probably a model + // binding and we do not want to mess with those; otherwise, we resolve it here. + if ($className && ! $this->alreadyInParameters($className, $parameters)) { + return $parameter->isDefaultValueAvailable() ? null : $this->container->make($className); + } + + return $skippableValue; + } + + /** + * Determine if an object of the given class is in a list of parameters. + * + * @param string $class + * @param array $parameters + * @return bool + */ + protected function alreadyInParameters($class, array $parameters) + { + return ! is_null(Arr::first($parameters, fn ($value) => $value instanceof $class)); + } + + /** + * Splice the given value into the parameter list. + * + * @param array $parameters + * @param string $offset + * @param mixed $value + * @return void + */ + protected function spliceIntoParameters(array &$parameters, $offset, $value) + { + array_splice( + $parameters, $offset, 0, [$value] + ); + } +} diff --git a/src/Illuminate/Routing/Router.php b/src/Illuminate/Routing/Router.php index 002ebaa..c5ba216 100644 --- a/src/Illuminate/Routing/Router.php +++ b/src/Illuminate/Routing/Router.php @@ -857,7 +857,11 @@ protected function createRoute($methods, $uri, $action) */ protected function newRoute($methods, $uri, $action) { - return new Route($methods, $uri, $action); + $route = new Route($methods, $uri, $action); + + $route->setContainer($this->container); + + return $route; } /** diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php new file mode 100644 index 0000000..abf68ea --- /dev/null +++ b/src/Illuminate/Support/Reflector.php @@ -0,0 +1,142 @@ +isPublic(); + } + + if (is_object($var[0]) && method_exists($class, '__call')) { + return (new ReflectionMethod($class, '__call'))->isPublic(); + } + + if (! is_object($var[0]) && method_exists($class, '__callStatic')) { + return (new ReflectionMethod($class, '__callStatic'))->isPublic(); + } + + return false; + } + + /** + * Get the class name of the given parameter's type, if possible. + * + * @param \ReflectionParameter $parameter + * @return string|null + */ + public static function getParameterClassName($parameter) + { + $type = $parameter->getType(); + + if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) { + return; + } + + return static::getTypeName($parameter, $type); + } + + /** + * Get the class names of the given parameter's type, including union types. + * + * @param \ReflectionParameter $parameter + * @return array + */ + public static function getParameterClassNames($parameter) + { + $type = $parameter->getType(); + + if (! $type instanceof ReflectionUnionType) { + return array_filter([static::getParameterClassName($parameter)]); + } + + $unionTypes = []; + + foreach ($type->getTypes() as $listedType) { + if (! $listedType instanceof ReflectionNamedType || $listedType->isBuiltin()) { + continue; + } + + $unionTypes[] = static::getTypeName($parameter, $listedType); + } + + return array_filter($unionTypes); + } + + /** + * Get the given type's class name. + * + * @param \ReflectionParameter $parameter + * @param \ReflectionNamedType $type + * @return string + */ + protected static function getTypeName($parameter, $type) + { + $name = $type->getName(); + + if (! is_null($class = $parameter->getDeclaringClass())) { + if ($name === 'self') { + return $class->getName(); + } + + if ($name === 'parent' && $parent = $class->getParentClass()) { + return $parent->getName(); + } + } + + return $name; + } + + /** + * Determine if the parameter's type is a subclass of the given type. + * + * @param \ReflectionParameter $parameter + * @param string $className + * @return bool + */ + public static function isParameterSubclassOf($parameter, $className) + { + $paramClassName = static::getParameterClassName($parameter); + + return $paramClassName + && (class_exists($paramClassName) || interface_exists($paramClassName)) + && (new ReflectionClass($paramClassName))->isSubclassOf($className); + } +}