1+ <?php
2+
3+ namespace Run \Steps \Container ;
4+
5+ use Psr \Container \ContainerInterface ;
6+ use Run \Exceptions \Container \ContainerException ;
7+
8+ class Container implements ContainerInterface
9+ {
10+ /**
11+ * The current globally available container (if any).
12+ *
13+ * @var static
14+ */
15+ protected static $ instance ;
16+
17+ private array $ entries = [];
18+
19+ /**
20+ * Get the globally available instance of the container.
21+ *
22+ * @return static
23+ */
24+ public static function getInstance ()
25+ {
26+ if (is_null (static ::$ instance )) {
27+ static ::$ instance = new static ;
28+ }
29+
30+ return static ::$ instance ;
31+ }
32+
33+ /**
34+ * Set the shared instance of the container.
35+ *
36+ */
37+ public static function setInstance (ContainerInterface $ container = null )
38+ {
39+ return static ::$ instance = $ container ;
40+ }
41+
42+
43+
44+ /**
45+ * Resolve the given type from the container.
46+ *
47+ * @param string|callable $abstract
48+ * @param array $parameters
49+ * @return mixed
50+ *
51+ * @throws \Run\Exceptions\Container\BindingResolutionException
52+ */
53+ public function make ($ abstract , array $ parameters = [])
54+ {
55+ return $ this ->resolve ($ abstract , $ parameters );
56+ }
57+
58+ public function get (string $ id )
59+ {
60+ if ($ this ->has ($ id )) {
61+ $ entry = $ this ->entries [$ id ];
62+
63+ return $ entry ($ this );
64+ }
65+
66+ return $ this ->resolve ($ id );
67+ }
68+
69+ public function has (string $ id ): bool
70+ {
71+ return isset ($ this ->entries [$ id ]);
72+ }
73+
74+ public function set (string $ id , callable $ concrete ): void
75+ {
76+ $ this ->entries [$ id ] = $ concrete ;
77+ }
78+
79+
80+
81+ protected function resolve (string $ id ,$ parameters = [])
82+ {
83+ // 1. Inspect the class that we are trying to get from the container
84+ $ reflectionClass = new \ReflectionClass ($ id );
85+
86+ if (! $ reflectionClass ->isInstantiable ()) {
87+ throw new ContainerException ('Class " ' . $ id . '" is not instantiable ' );
88+ }
89+
90+ // 2. Inspect the constructor of the class
91+ $ constructor = $ reflectionClass ->getConstructor ();
92+
93+ if (! $ constructor ) {
94+ return new $ id ;
95+ }
96+
97+ // 3. Inspect the constructor parameters (dependencies)
98+ $ parameters = $ constructor ->getParameters ();
99+
100+ if (! $ parameters ) {
101+ return new $ id ;
102+ }
103+
104+ // 4. If the constructor parameter is a class then try to resolve that class using the container
105+ $ dependencies = array_map (function (\ReflectionParameter $ param ) use ($ id ) {
106+ $ type = $ param ->getType ();
107+ $ name = $ param ->getName ();
108+
109+ if (! $ type ) throw new ContainerException (
110+ 'Failed to resolve class " ' . $ id . '" because param " ' . $ name . '" is missing a type hint '
111+ );
112+
113+
114+ if ($ type instanceof \ReflectionUnionType) throw new ContainerException (
115+ 'Failed to resolve class " ' . $ id . '" because of union type for param " ' . $ name . '" '
116+ );
117+
118+
119+ if ($ type instanceof \ReflectionNamedType && ! $ type ->isBuiltin ()) {
120+ return $ this ->get ($ type ->getName ());
121+ }
122+
123+ throw new ContainerException (
124+ 'Failed to resolve class " ' . $ id . '" because invalid param " ' . $ name . '" '
125+ );
126+ }, $ parameters
127+ );
128+
129+ return $ reflectionClass ->newInstanceArgs ($ dependencies );
130+ }
131+ }
0 commit comments