├── .gitignore ├── CHANGELOG.md ├── README.md ├── composer.json └── src ├── Container.php ├── ContainerFactory.php ├── Exception ├── ContainerException.php └── NotFoundException.php └── Traits ├── Method.php └── Parameters.php /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raylin666/php-container/bf0a8cd835bdb911a5eff2b1c1743559749cb12b/.gitignore -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | v1.1.0 4 | * 容器工厂 `ContainerFactory` 在未实例化情况下, 直接调用 `getContainer` 方法时提供 是否自动生成默认初始化容器参数 5 | 6 | v1.0.0 IOC 对象容器服务首发 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IOC 对象容器服务 2 | 3 | [![GitHub release](https://img.shields.io/github/v/release/raylin666/php-container.svg)](https://github.com/raylin666/php-container/releases) 4 | [![PHP version](https://img.shields.io/badge/php-%3E%207.2-orange.svg)](https://github.com/php/php-src) 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](#LICENSE) 6 | 7 | ### 环境要求 8 | 9 | * PHP >=7.2 10 | 11 | ### 安装说明 12 | 13 | ``` 14 | composer require "raylin666/container" 15 | ``` 16 | 17 | ### 使用方式 18 | 19 | #### 新增服务类 20 | ```php 21 | bind(DateTime::class, DateTime::class); 32 | 33 | // 是否有绑定该容器 34 | $container->has(DateTime::class); 35 | 36 | // 实例化容器 37 | $container->make(DateTime::class, ['timezone' => new DateTimeZone('UTC')]); 38 | 39 | $container->bind('datetimezone', function () { 40 | return new DateTimeZone('UTC'); 41 | }); 42 | 43 | // 获取容器, 优先查看是否有已实例化的容器, 如果有则直接取出, 如果没有则实例化容器(make)并返回 44 | $container->get('datetimezone'); 45 | // 为容器设置别名, 一般情况下该别名可用来作为装饰者, 因为本身就是装饰器设计模式, 比如 laravel 的 alias 就类似该原理 46 | $container->alias('tzone', 'datetimezone'); 47 | 48 | $container->get('tzone'); 49 | 50 | $container->bind(DateTimeZone::class, DateTimeZone::class); 51 | 52 | $container->make(DateTimeZone::class, ['timezone' => 'PRC']); 53 | 54 | // ... 更多功能可阅读源码 55 | 56 | ``` 57 | 58 | ## 更新日志 59 | 60 | 请查看 [CHANGELOG.md](CHANGELOG.md) 61 | 62 | ### 联系 63 | 64 | 如果你在使用中遇到问题,请联系: [1099013371@qq.com](mailto:1099013371@qq.com). 博客: [kaka 梦很美](http://www.ls331.com) 65 | 66 | ## License MIT 67 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raylin666/container", 3 | "description": "IOC object container service", 4 | "keywords": [ 5 | "sdk", 6 | "container", 7 | "raylin666", 8 | "ioc", 9 | "php", 10 | "object", 11 | "di" 12 | ], 13 | "type": "library", 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "raylin666", 18 | "email": "1099013371@qq.com" 19 | } 20 | ], 21 | "require": { 22 | "php": ">=7.2", 23 | "raylin666/contract": "^1.0" 24 | }, 25 | "require-dev": {}, 26 | "autoload": { 27 | "files": [], 28 | "psr-4": { 29 | "Raylin666\\Container\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": {} 34 | }, 35 | "packages": {}, 36 | "packages-dev": {} 37 | } 38 | -------------------------------------------------------------------------------- /src/Container.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Raylin666\Container; 13 | 14 | use Closure; 15 | use TypeError; 16 | use Exception; 17 | use LogicException; 18 | use ReflectionClass; 19 | use ReflectionException; 20 | use Raylin666\Container\Traits\Method; 21 | use Raylin666\Container\Traits\Parameters; 22 | use Raylin666\Contract\ContainerInterface; 23 | use Raylin666\Contract\ServiceProviderInterface; 24 | use Raylin666\Container\Exception\NotFoundException; 25 | use Raylin666\Container\Exception\ContainerException; 26 | 27 | /** 28 | * Class Container 29 | * @package Raylin666\Container 30 | */ 31 | class Container implements ContainerInterface 32 | { 33 | use Parameters, 34 | Method; 35 | 36 | /** 37 | * 绑定 38 | * @var array[] 39 | */ 40 | protected $bindings = []; 41 | 42 | /** 43 | * 存放解析过的具体类 44 | * @var bool[] 45 | */ 46 | protected $resolved = []; 47 | 48 | /** 49 | * @var object[] 50 | */ 51 | protected $instances = []; 52 | 53 | /** 54 | * 服务容器 55 | * @var ServiceProviderInterface[] 56 | */ 57 | protected $providers = []; 58 | 59 | /** 60 | * 别名 61 | * @var array 62 | */ 63 | protected $alias = []; 64 | 65 | /** 66 | * 向容器注册绑定 67 | * @param string $abstract 68 | * @param Closure|string|null $concrete 69 | * @param bool $singleton 70 | * @return mixed|void 71 | */ 72 | public function bind($abstract, $concrete = null, $singleton = false) 73 | { 74 | // TODO: Implement bind() method. 75 | 76 | $this->forgetInstance($abstract); 77 | 78 | is_null($concrete) && $concrete = $abstract; 79 | 80 | if (! $concrete instanceof Closure) { 81 | // $concrete 只能是 Closure|string|null 数据类型 82 | if (! is_string($concrete)) { 83 | throw new TypeError(self::class . '::bind(): Argument #2 ($concrete) must be of type Closure|string|null'); 84 | } 85 | 86 | // 组装成一个闭包 87 | $concrete = $this->generateClosure($abstract, $concrete); 88 | } 89 | 90 | $this->bindings[$abstract] = compact('concrete', 'singleton'); 91 | } 92 | 93 | /** 94 | * 如果容器未注册绑定,将会完成注册 95 | * @param string $abstract 96 | * @param Closure|string|null $concrete 97 | */ 98 | public function bindIf($abstract, $concrete = null) 99 | { 100 | ($this->has($abstract) == false) && $this->bind($abstract, $concrete); 101 | } 102 | 103 | /** 104 | * 类静态(将已实例的类保存到容器) 105 | * @param string $abstract 106 | * @param Closure|string|null $concrete 107 | * @return mixed|void 108 | */ 109 | public function singleton($abstract, $concrete = null) 110 | { 111 | // TODO: Implement singleton() method. 112 | 113 | $this->bind($abstract, $concrete, true); 114 | } 115 | 116 | /** 117 | * 如果容器未注册绑定类静态,将会完成注册(将已实例的类保存到容器) 118 | * @param string $abstract 119 | * @param Closure|string|null $concrete 120 | */ 121 | public function singletonIf($abstract, $concrete = null) 122 | { 123 | ($this->has($abstract) == false) && $this->bind($abstract, $concrete, true); 124 | } 125 | 126 | /** 127 | * 判断容器绑定实例是否存在 128 | * @param string $id 129 | * @return bool 130 | */ 131 | public function has($id) 132 | { 133 | // TODO: Implement has() method. 134 | 135 | return isset($this->bindings[$id]) || isset($this->instances[$id]) || $this->isAlias($id); 136 | } 137 | 138 | /** 139 | * 获取容器绑定实例 140 | * @param string $id 141 | * @return mixed|object|void 142 | * @throws Exception 143 | */ 144 | public function get($id) 145 | { 146 | // TODO: Implement get() method. 147 | 148 | try { 149 | return $this->make($id); 150 | } catch (Exception $e) { 151 | if ($this->has($id)) throw $e; 152 | throw new NotFoundException($id, $e->getCode(), $e); 153 | } 154 | } 155 | 156 | /** 157 | * 从容器解析给定类型 158 | * @param callable|string $abstract 159 | * @param array $parameters 160 | * @return mixed|object|void 161 | * @throws ContainerException 162 | * @throws ReflectionException 163 | */ 164 | public function make($abstract, array $parameters = []) 165 | { 166 | // TODO: Implement make() method. 167 | 168 | $abstract = $this->getAlias($abstract); 169 | 170 | // array_key_exists - $key 参数只能是 int|string|null 171 | if (is_numeric($abstract) || is_string($abstract) || is_null($abstract)) { 172 | if (array_key_exists($abstract, $this->instances)) return $this->instances[$abstract]; 173 | } 174 | 175 | $this->parametersStack = $parameters; 176 | 177 | $concrete = $this->getConcrete($abstract); 178 | 179 | $object = $this->isBuildable($concrete, $abstract) ? $this->build($concrete) : $this->make($concrete); 180 | 181 | if ($this->isSingleton($abstract)) { 182 | $this->instances[$abstract] = $object; 183 | } 184 | 185 | if (is_string($abstract)) { 186 | $this->resolved[$abstract] = true; 187 | } 188 | 189 | $this->parametersStack = []; 190 | 191 | return $object; 192 | } 193 | 194 | /** 195 | * 确定给定的抽象类型是否已解析 196 | * @param string $abstract 197 | * @return bool 198 | */ 199 | public function isResolved($abstract): bool 200 | { 201 | return isset($this->resolved[$this->getAlias($abstract)]); 202 | } 203 | 204 | /** 205 | * 是否静态实例 206 | * @param $abstract 207 | * @return bool 208 | */ 209 | public function isSingleton($abstract) 210 | { 211 | return isset($this->instances[$abstract]) || 212 | (isset($this->bindings[$abstract]['singleton']) && 213 | $this->bindings[$abstract]['singleton'] === true); 214 | } 215 | 216 | /** 217 | * 包装给定的闭包,以便在执行时注入其依赖项 218 | * @param Closure $callback 219 | * @param array $parameters 220 | * @return Closure 221 | */ 222 | public function wrap(Closure $callback, array $parameters = []) 223 | { 224 | return function () use ($callback, $parameters) { 225 | return $this->call($callback, $parameters); 226 | }; 227 | } 228 | 229 | /** 230 | * 函数方法执行调用 231 | * @param $callback 232 | * @param array $parameters 233 | * @param null $defaultMethod 234 | * @return mixed 235 | */ 236 | public function call($callback, array $parameters = [], $defaultMethod = null) 237 | { 238 | if (is_string($callback) && ! $defaultMethod && method_exists($callback, '__invoke')) { 239 | $defaultMethod = '__invoke'; 240 | } 241 | 242 | if ($this->isCallableWithAtSign($callback) || $defaultMethod) { 243 | return $this->callClass($callback, $parameters, $defaultMethod); 244 | } 245 | 246 | return $this->callMethod($callback, function () use ($callback, $parameters) { 247 | return $callback( 248 | ...array_values( 249 | $this->getMethodDependencies($callback, $parameters) 250 | ) 251 | ); 252 | }); 253 | } 254 | 255 | /** 256 | * 实例化给定类型的具体实例 257 | * @param Closure|string $concrete 258 | * @return mixed|object|void 259 | * @throws ContainerException 260 | */ 261 | public function build($concrete) 262 | { 263 | return ($concrete instanceof Closure) ? $this->reflectorFunction($concrete) : $this->reflectorClass($concrete); 264 | } 265 | 266 | /** 267 | * 获取一个闭包来解析容器中给定的类型 268 | * @param string $abstract 269 | * @return Closure 270 | */ 271 | public function factory($abstract) 272 | { 273 | return function () use ($abstract) { 274 | return $this->make($abstract); 275 | }; 276 | } 277 | 278 | /** 279 | * 注册容器服务 280 | * @param ServiceProviderInterface $provider 281 | */ 282 | public function register(ServiceProviderInterface $provider) 283 | { 284 | $provider->register($this); 285 | $this->providers[get_class($provider)] = $provider; 286 | } 287 | 288 | /** 289 | * 设置别名 290 | * @param string $alias 291 | * @param $abstract 292 | */ 293 | public function alias(string $alias, $abstract) 294 | { 295 | if ($alias === $abstract) { 296 | throw new LogicException("[{$abstract}] is aliased to itself."); 297 | } 298 | 299 | $this->alias[$alias] = $abstract; 300 | } 301 | 302 | /** 303 | * 别名是否存在 304 | * @param string $name 305 | * @return bool 306 | */ 307 | public function isAlias($name): bool 308 | { 309 | return is_string($name) && isset($this->alias[$name]); 310 | } 311 | 312 | /** 313 | * 获取别名 314 | * @param string $abstract 315 | * @return mixed|string 316 | */ 317 | public function getAlias($abstract) 318 | { 319 | return $this->isAlias($abstract) ? $this->alias[$abstract] : $abstract; 320 | } 321 | 322 | /** 323 | * clear all containers 324 | */ 325 | public function flush() 326 | { 327 | $this->bindings = $this->resolved = $this->providers = $this->instances = $this->alias = []; 328 | } 329 | 330 | /** 331 | * 获取生成类型时要使用的闭包, 对应 parseClosure 方法 332 | * @param string $abstract 333 | * @param string $concrete 334 | * @return Closure 335 | */ 336 | protected function generateClosure($abstract, $concrete) 337 | { 338 | /** @var $container ContainerInterface */ 339 | return function ($container, $parameters = []) use ($abstract, $concrete) { 340 | if ($abstract == $concrete) { 341 | return $container->build($concrete); 342 | } 343 | 344 | return $container->make($concrete, $parameters); 345 | }; 346 | } 347 | 348 | /** 349 | * 解析生成类型时对应的闭包, 对应 generateClosure 方法 350 | * @param Closure $concrete 351 | * @param ContainerInterface $container 352 | * @param array $parameters 353 | * @return mixed 354 | */ 355 | protected function parseClosure(Closure $concrete, ContainerInterface $container, array $parameters = []) 356 | { 357 | return $concrete($container, $parameters); 358 | } 359 | 360 | /** 361 | * 获取具体实例对象 362 | * @param string $abstract 363 | * @return mixed 364 | */ 365 | protected function getConcrete($abstract) 366 | { 367 | return (is_string($abstract) && isset($this->bindings[$abstract])) ? $this->bindings[$abstract]['concrete'] : $abstract; 368 | } 369 | 370 | /** 371 | * 确定给定的抽象是否可建造 372 | * @param $concrete 373 | * @param string $abstract 374 | * @return bool 375 | */ 376 | protected function isBuildable($concrete, $abstract) 377 | { 378 | return ($concrete === $abstract) || ($concrete instanceof Closure); 379 | } 380 | 381 | /** 382 | * 去除类静态 383 | * @param $abstract 384 | */ 385 | protected function forgetInstance($abstract) 386 | { 387 | unset($this->instances[$abstract]); 388 | } 389 | 390 | /** 391 | * 解析类对象实例 392 | * @param string $concrete 393 | * @return object|void 394 | * @throws ContainerException 395 | * @throws ReflectionException 396 | */ 397 | protected function reflectorClass(string $concrete) 398 | { 399 | try { 400 | $reflector = new ReflectionClass($concrete); 401 | } catch (ReflectionException $e) { 402 | throw new ContainerException("Target class [$concrete] does not exist.", 0, $e); 403 | } 404 | 405 | // 是否可被实例 抽象类和接口类是不能被实例化的 406 | if (! $reflector->isInstantiable()) { 407 | return $this->notInstantiable($concrete); 408 | } 409 | 410 | // 获取构造函数 411 | $constructor = $reflector->getConstructor(); 412 | if (is_null($constructor)) { 413 | return $reflector->newInstance(); 414 | } 415 | 416 | // 获取构造函数参数 417 | $dependencies = $constructor->getParameters(); 418 | 419 | try { 420 | // 参数解析 421 | $instances = $this->resolveDependencies($dependencies); 422 | } catch (ContainerException $e) { 423 | throw $e; 424 | } 425 | 426 | // 实例化对象 427 | return $reflector->newInstanceArgs($instances); 428 | } 429 | 430 | /** 431 | * 解析方法 Closure 实例 432 | * @param Closure $concrete 433 | * @return mixed 434 | */ 435 | protected function reflectorFunction(Closure $concrete) 436 | { 437 | return $this->parseClosure($concrete, $this, $this->parametersStack); 438 | } 439 | 440 | /** 441 | * 抛出一个不可实例化的异常 442 | * @param $concrete 443 | * @throws ContainerException 444 | */ 445 | protected function notInstantiable($concrete) 446 | { 447 | throw new ContainerException("Target [$concrete] is not instantiable."); 448 | } 449 | 450 | /** 451 | * @return array 452 | */ 453 | public function getBindings(): array 454 | { 455 | return $this->bindings; 456 | } 457 | 458 | /** 459 | * @return bool[] 460 | */ 461 | public function getResolved(): array 462 | { 463 | return $this->resolved; 464 | } 465 | 466 | /** 467 | * @return ServiceProviderInterface[] 468 | */ 469 | public function getProviders(): array 470 | { 471 | return $this->providers; 472 | } 473 | 474 | /** 475 | * @return array 476 | */ 477 | public function getAliasList(): array 478 | { 479 | return $this->alias; 480 | } 481 | } -------------------------------------------------------------------------------- /src/ContainerFactory.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Raylin666\Container; 13 | 14 | use Raylin666\Contract\ContainerInterface; 15 | use Raylin666\Contract\FactoryInterface; 16 | 17 | /** 18 | * Class ContainerFactory 19 | * @package Raylin666\Container 20 | */ 21 | class ContainerFactory implements FactoryInterface 22 | { 23 | /** 24 | * @var ContainerInterface 25 | */ 26 | private static $container; 27 | 28 | /** 29 | * ContainerFactory constructor. 30 | * @param ContainerInterface $container 31 | */ 32 | public function __construct(ContainerInterface $container) 33 | { 34 | self::setContainer($container); 35 | } 36 | 37 | /** 38 | * @param bool $notHasGenerate 当容器不存在时, 是否生成默认初始化容器 39 | * @return ContainerInterface 40 | */ 41 | public static function getContainer(bool $notHasGenerate = true): ContainerInterface 42 | { 43 | // 如果不实例化该类, 并直接通过 static::getContainer 方法获取容器时, 可选择是否自动初始化容器 44 | return (empty(self::hasContainer()) && $notHasGenerate) ? self::setContainer(make(Container::class)) : self::$container; 45 | } 46 | 47 | /** 48 | * @return bool 49 | */ 50 | public static function hasContainer(): bool 51 | { 52 | return isset(self::$container); 53 | } 54 | 55 | /** 56 | * @param ContainerInterface $container 57 | * @return ContainerInterface 58 | */ 59 | public static function setContainer(ContainerInterface $container): ContainerInterface 60 | { 61 | return self::$container = $container; 62 | } 63 | } -------------------------------------------------------------------------------- /src/Exception/ContainerException.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Raylin666\Container\Exception; 13 | 14 | use Exception; 15 | use Psr\Container\ContainerExceptionInterface; 16 | 17 | /** 18 | * Class ContainerException 19 | * @package Raylin666\Container\Exception 20 | */ 21 | class ContainerException extends Exception implements ContainerExceptionInterface 22 | { 23 | 24 | } -------------------------------------------------------------------------------- /src/Exception/NotFoundException.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Raylin666\Container\Exception; 13 | 14 | use RuntimeException; 15 | use Psr\Container\NotFoundExceptionInterface; 16 | 17 | /** 18 | * Class NotFoundException 19 | * @package Raylin666\Container\Exception 20 | */ 21 | class NotFoundException extends RuntimeException implements NotFoundExceptionInterface 22 | { 23 | 24 | } -------------------------------------------------------------------------------- /src/Traits/Method.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Raylin666\Container\Traits; 13 | 14 | use Closure; 15 | use ReflectionMethod; 16 | use ReflectionFunction; 17 | use ReflectionParameter; 18 | use InvalidArgumentException; 19 | use Raylin666\Container\Exception\ContainerException; 20 | 21 | /** 22 | * Trait Method 23 | * @package Raylin666\Container\Traits 24 | */ 25 | trait Method 26 | { 27 | /** 28 | * 使用调用对类的字符串引用类@方法语法 29 | * @param $target 30 | * @param array $parameters 31 | * @param null $defaultMethod 32 | * @return mixed 33 | */ 34 | protected function callClass($target, array $parameters = [], $defaultMethod = null) 35 | { 36 | $segments = explode('@', $target); 37 | 38 | $method = count($segments) === 2 ? $segments[1] : $defaultMethod; 39 | if (is_null($method)) { 40 | throw new InvalidArgumentException('Method not provided.'); 41 | } 42 | 43 | return $this->call([$this->make($segments[0]), $method], $parameters); 44 | } 45 | 46 | /** 47 | * 执行函数方法 48 | * @param $callback 49 | * @param $default 50 | * @return mixed 51 | */ 52 | protected function callMethod($callback, $default) 53 | { 54 | if (! is_array($callback)) { 55 | return $default instanceof Closure ? $default() : $default; 56 | } 57 | 58 | $method = $this->normalizeMethod($callback); 59 | 60 | return call_user_func($method, $callback[0], $this); 61 | } 62 | 63 | /** 64 | * 获取给定方法的所有依赖项 65 | * @param $callback 66 | * @param array $parameters 67 | * @return array 68 | * @throws ContainerException 69 | * @throws \ReflectionException 70 | */ 71 | protected function getMethodDependencies($callback, array $parameters = []) 72 | { 73 | $dependencies = []; 74 | 75 | foreach ($this->getCallReflector($callback)->getParameters() as $parameter) { 76 | $this->addDependencyForCallParameter($parameter, $parameters, $dependencies); 77 | } 78 | 79 | return array_merge($dependencies, array_values($parameters)); 80 | } 81 | 82 | /** 83 | * 为给定回调获取正确的反射实例 84 | * @param $callback 85 | * @return ReflectionFunction|ReflectionMethod 86 | * @throws \ReflectionException 87 | */ 88 | protected function getCallReflector($callback) 89 | { 90 | if (is_string($callback) && strpos($callback, '::') !== false) { 91 | $callback = explode('::', $callback); 92 | } elseif (is_object($callback) && ! $callback instanceof Closure) { 93 | $callback = [$callback, '__invoke']; 94 | } 95 | 96 | return is_array($callback) 97 | ? new ReflectionMethod($callback[0], $callback[1]) 98 | : new ReflectionFunction($callback); 99 | } 100 | 101 | /** 102 | * 获取给定调用参数的依赖关系 103 | * @param ReflectionParameter $parameter 104 | * @param array $parameters 105 | * @param $dependencies 106 | * @throws ContainerException 107 | * @throws \ReflectionException 108 | */ 109 | protected function addDependencyForCallParameter(ReflectionParameter $parameter, array &$parameters, &$dependencies) 110 | { 111 | if (array_key_exists($paramName = $parameter->getName(), $parameters)) { 112 | $dependencies[] = $parameters[$paramName]; 113 | unset($parameters[$paramName]); 114 | } elseif (! is_null($className = $this->getParameterClassName($parameter))) { 115 | if (array_key_exists($className, $parameters)) { 116 | $dependencies[] = $parameters[$className]; 117 | unset($parameters[$className]); 118 | } else { 119 | $dependencies[] = $this->make($className); 120 | } 121 | } elseif ($parameter->isDefaultValueAvailable()) { 122 | $dependencies[] = $parameter->getDefaultValue(); 123 | } elseif (! $parameter->isOptional() && ! array_key_exists($paramName, $parameters)) { 124 | throw new ContainerException("Unable to resolve dependency [{$parameter}]"); 125 | } 126 | } 127 | 128 | /** 129 | * 将给定回调规范化为类@方法一串 130 | * @param $callback 131 | * @return string 132 | */ 133 | protected function normalizeMethod($callback) 134 | { 135 | $class = is_string($callback[0]) ? $callback[0] : get_class($callback[0]); 136 | return "{$class}@{$callback[1]}"; 137 | } 138 | 139 | /** 140 | * 确定给定字符串是否在类@方法语法 141 | * @param $callback 142 | * @return bool 143 | */ 144 | protected function isCallableWithAtSign($callback) 145 | { 146 | return is_string($callback) && strpos($callback, '@') !== false; 147 | } 148 | } -------------------------------------------------------------------------------- /src/Traits/Parameters.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Raylin666\Container\Traits; 13 | 14 | use ReflectionException; 15 | use ReflectionNamedType; 16 | use ReflectionParameter; 17 | use Raylin666\Container\Exception\ContainerException; 18 | 19 | /** 20 | * Trait Parameters 21 | * @package Raylin666\Container\Traits 22 | */ 23 | trait Parameters 24 | { 25 | /** 26 | * 参数栈 27 | * @var array 28 | */ 29 | protected $parametersStack = []; 30 | 31 | /** 32 | * 从 ReflectionParameters 解析所有依赖项 33 | * @param ReflectionParameter[] $dependencies 34 | * @return array 35 | * @throws ContainerException 36 | * @throws ReflectionException 37 | */ 38 | protected function resolveDependencies(array $dependencies) 39 | { 40 | $results = []; 41 | 42 | foreach ($dependencies as $dependency) { 43 | if ($parameterOverride = $this->getParameterOverride($dependency)) { 44 | $results[] = $parameterOverride; 45 | continue ; 46 | } 47 | 48 | $result = is_null($this->getParameterClassName($dependency)) 49 | ? $this->resolvePrimitive($dependency) 50 | : $this->resolveClass($dependency); 51 | 52 | if ($dependency->isVariadic()) { 53 | $results = array_merge($results, $result); 54 | } else { 55 | $results[] = $result; 56 | } 57 | } 58 | 59 | return $results; 60 | } 61 | 62 | /** 63 | * 获取给定参数类型的类名 64 | * @param ReflectionParameter $parameter 65 | * @return string|void 66 | */ 67 | protected function getParameterClassName(ReflectionParameter $parameter) 68 | { 69 | $type = $parameter->getType(); 70 | 71 | if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) return; 72 | 73 | $name = $type->getName(); 74 | 75 | if (! is_null($class = $parameter->getDeclaringClass())) { 76 | if ($name === 'self') { 77 | return $class->getName(); 78 | } 79 | 80 | if ($name === 'parent' && $parent = $class->getParentClass()) { 81 | return $parent->getName(); 82 | } 83 | } 84 | 85 | return $name; 86 | } 87 | 88 | /** 89 | * 解析非类的基元依赖项 90 | * @param ReflectionParameter $parameter 91 | * @return mixed 92 | * @throws ContainerException 93 | * @throws ReflectionException 94 | */ 95 | protected function resolvePrimitive(ReflectionParameter $parameter) 96 | { 97 | if ($parameter->isDefaultValueAvailable()) { 98 | return $parameter->getDefaultValue(); 99 | } 100 | 101 | if ($parameter->isOptional()) return; 102 | 103 | $message = $parameter->getDeclaringClass() 104 | ? "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}" 105 | : "Unresolvable dependency resolving [$parameter]"; 106 | 107 | throw new ContainerException($message); 108 | } 109 | 110 | /** 111 | * 从容器中解析基于类的依赖项 112 | * @param ReflectionParameter $parameter 113 | * @return array|mixed|void 114 | * @throws ContainerException 115 | */ 116 | protected function resolveClass(ReflectionParameter $parameter) 117 | { 118 | try { 119 | return $this->resolveVariadicClass($parameter); 120 | } catch (ContainerException $e) { 121 | if ($parameter->isDefaultValueAvailable()) { 122 | return $parameter->getDefaultValue(); 123 | } 124 | 125 | if ($parameter->isVariadic()) { 126 | return []; 127 | } 128 | 129 | throw $e; 130 | } 131 | } 132 | 133 | /** 134 | * 从容器中解析基于类的变量依赖项 135 | * @param ReflectionParameter $parameter 136 | * @return mixed|void 137 | * @throws ContainerException 138 | */ 139 | protected function resolveVariadicClass(ReflectionParameter $parameter) 140 | { 141 | return $this->make($this->getParameterClassName($parameter)); 142 | } 143 | 144 | /** 145 | * 确定给定的依赖项是否有参数重写 146 | * @param ReflectionParameter $dependency 147 | * @return bool 148 | */ 149 | protected function hasParameterOverride(ReflectionParameter $dependency) 150 | { 151 | return array_key_exists($dependency->name, $this->parametersStack); 152 | } 153 | 154 | /** 155 | * 获取依赖项的参数重写 156 | * @param ReflectionParameter $dependency 157 | * @return mixed 158 | */ 159 | protected function getParameterOverride(ReflectionParameter $dependency) 160 | { 161 | return $this->hasParameterOverride($dependency) ? $this->parametersStack[$dependency->name] : null; 162 | } 163 | } --------------------------------------------------------------------------------