1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the Koded package. |
||
5 | * |
||
6 | * (c) Mihail Binev <[email protected]> |
||
7 | * |
||
8 | * Please view the LICENSE distributed with this source code |
||
9 | * for the full copyright and license information. |
||
10 | * |
||
11 | */ |
||
12 | |||
13 | namespace Koded; |
||
14 | |||
15 | use Psr\Container\{ContainerExceptionInterface, NotFoundExceptionInterface}; |
||
16 | use LogicException; |
||
17 | use ReflectionClass; |
||
18 | use ReflectionException; |
||
19 | use ReflectionParameter; |
||
20 | use Throwable; |
||
21 | use function array_filter; |
||
22 | use function join; |
||
23 | use function strtr; |
||
24 | |||
25 | class DIException extends LogicException implements ContainerExceptionInterface |
||
26 | { |
||
27 | public const |
||
28 | E_CIRCULAR_DEPENDENCY = 7001, |
||
29 | E_NON_PUBLIC_METHOD = 7002, |
||
30 | E_CANNOT_INSTANTIATE = 7003, |
||
31 | E_INVALID_PARAMETER_NAME = 7004, |
||
32 | E_INSTANCE_NOT_FOUND = 7005, |
||
33 | E_MISSING_ARGUMENT = 7006, |
||
34 | E_REFLECTION_ERROR = 7007, |
||
35 | E_CANNOT_BIND_INTERFACE = 7008; |
||
36 | |||
37 | protected array $messages = [ |
||
38 | self::E_CIRCULAR_DEPENDENCY => 'Circular dependency detected while creating an instance for :class', |
||
39 | self::E_NON_PUBLIC_METHOD => 'Failed to create an instance, because the method ":class:::method" is not public', |
||
40 | self::E_CANNOT_INSTANTIATE => 'Cannot instantiate :type :name', |
||
41 | self::E_INVALID_PARAMETER_NAME => 'Provide a valid name for the global parameter: ":name"', |
||
42 | self::E_INSTANCE_NOT_FOUND => 'The requested instance :id is not found in the container', |
||
43 | self::E_MISSING_ARGUMENT => 'Required parameter ":name" is missing at position :position in :function()', |
||
44 | self::E_CANNOT_BIND_INTERFACE => 'Only interface to class binding is allowed. Cannot bind interface ":dependency" to interface ":interface"', |
||
45 | self::E_REFLECTION_ERROR => ':message', |
||
46 | ]; |
||
47 | |||
48 | 18 | public function __construct(int $code, array $arguments = [], Throwable $previous = null) |
|
49 | { |
||
50 | 18 | parent::__construct( |
|
51 | 18 | strtr($this->messages[$code] ?? ':message', $arguments + [':message' => $this->message]), |
|
52 | $code, |
||
53 | $previous |
||
54 | ); |
||
55 | 18 | } |
|
56 | |||
57 | 1 | public static function forCircularDependency(string $class): static |
|
58 | { |
||
59 | 1 | return new static(static::E_CIRCULAR_DEPENDENCY, [ |
|
60 | 1 | ':class' => $class |
|
61 | ]); |
||
62 | } |
||
63 | |||
64 | 1 | public static function forNonPublicMethod(string $class, string $method): static |
|
65 | { |
||
66 | 1 | return new static(static::E_NON_PUBLIC_METHOD, [ |
|
67 | 1 | ':class' => $class, |
|
68 | 1 | ':method' => $method |
|
69 | ]); |
||
70 | } |
||
71 | |||
72 | 5 | public static function cannotInstantiate(ReflectionClass $dependency): static |
|
73 | { |
||
74 | 5 | $type = match (true) { |
|
75 | 5 | $dependency->isInterface() => 'interface', |
|
76 | 3 | $dependency->isAbstract() => 'abstract class', |
|
77 | 1 | $dependency->isTrait() => 'trait', |
|
78 | // @codeCoverageIgnoreStart |
||
79 | default => 'class', |
||
80 | // @codeCoverageIgnoreEnd |
||
81 | }; |
||
82 | 5 | return new static(static::E_CANNOT_INSTANTIATE, [ |
|
83 | 5 | ':name' => $dependency->name, |
|
84 | 5 | ':type' => $type |
|
85 | ]); |
||
86 | } |
||
87 | |||
88 | 7 | public static function forInvalidParameterName(string $name): static |
|
89 | { |
||
90 | 7 | return new static(static::E_INVALID_PARAMETER_NAME, [ |
|
91 | 7 | ':name' => $name |
|
92 | ]); |
||
93 | } |
||
94 | |||
95 | 1 | public static function forMissingArgument( |
|
96 | string $name, |
||
97 | ReflectionParameter $parameter, |
||
98 | Throwable $previous = null): static |
||
99 | { |
||
100 | 1 | return new static(static::E_MISSING_ARGUMENT, [ |
|
101 | 1 | ':name' => $name, |
|
102 | 1 | ':position' => $parameter->getPosition(), |
|
103 | 1 | ':function' => join('::', array_filter([ |
|
104 | 1 | $parameter->getDeclaringClass()?->name, |
|
105 | 1 | $parameter->getDeclaringFunction()?->name |
|
106 | ])) |
||
107 | ], $previous); |
||
108 | } |
||
109 | |||
110 | 1 | public static function forReflectionError(ReflectionException $exception): static |
|
111 | { |
||
112 | 1 | return new static(static::E_REFLECTION_ERROR, [ |
|
113 | 1 | ':message' => $exception->getMessage() |
|
114 | ], $exception); |
||
115 | } |
||
116 | |||
117 | 1 | public static function forInterfaceBinding(string $dependency, string $interface): static |
|
118 | { |
||
119 | 1 | return new static(static::E_CANNOT_BIND_INTERFACE, [ |
|
120 | 1 | ':dependency' => $dependency, |
|
121 | 1 | ':interface' => $interface |
|
122 | ]); |
||
123 | } |
||
124 | } |
||
125 | |||
126 | |||
127 | class DIInstanceNotFound extends DIException implements NotFoundExceptionInterface |
||
128 | { |
||
129 | 1 | public static function for(string $id): NotFoundExceptionInterface |
|
130 | { |
||
131 | 1 | return new static(static::E_INSTANCE_NOT_FOUND, [':id' => $id]); |
|
132 | } |
||
133 | } |
||
134 |