8
8
use Illuminate \Contracts \Container \BindingResolutionException ;
9
9
use Illuminate \Contracts \Container \CircularDependencyException ;
10
10
use Illuminate \Contracts \Container \Container as ContainerContract ;
11
+ use Illuminate \Contracts \Container \ContextualAttribute ;
11
12
use LogicException ;
13
+ use ReflectionAttribute ;
12
14
use ReflectionClass ;
13
15
use ReflectionException ;
14
16
use ReflectionFunction ;
@@ -108,6 +110,13 @@ class Container implements ArrayAccess, ContainerContract
108
110
*/
109
111
public $ contextual = [];
110
112
113
+ /**
114
+ * The contextual attribute handlers.
115
+ *
116
+ * @var array[]
117
+ */
118
+ public $ contextualAttributes = [];
119
+
111
120
/**
112
121
* All of the registered rebound callbacks.
113
122
*
@@ -157,6 +166,13 @@ class Container implements ArrayAccess, ContainerContract
157
166
*/
158
167
protected $ afterResolvingCallbacks = [];
159
168
169
+ /**
170
+ * All of the after resolving attribute callbacks by class type.
171
+ *
172
+ * @var array[]
173
+ */
174
+ protected $ afterResolvingAttributeCallbacks = [];
175
+
160
176
/**
161
177
* Define a contextual binding.
162
178
*
@@ -174,6 +190,18 @@ public function when($concrete)
174
190
return new ContextualBindingBuilder ($ this , $ aliases );
175
191
}
176
192
193
+ /**
194
+ * Define a contextual binding based on an attribute.
195
+ *
196
+ * @param string $attribute
197
+ * @param \Closure $handler
198
+ * @return void
199
+ */
200
+ public function whenHasAttribute (string $ attribute , Closure $ handler )
201
+ {
202
+ $ this ->contextualAttributes [$ attribute ] = $ handler ;
203
+ }
204
+
177
205
/**
178
206
* Determine if the given abstract type has been bound.
179
207
*
@@ -923,7 +951,11 @@ public function build($concrete)
923
951
if (is_null ($ constructor )) {
924
952
array_pop ($ this ->buildStack );
925
953
926
- return new $ concrete ;
954
+ $ this ->fireAfterResolvingAttributeCallbacks (
955
+ $ reflector ->getAttributes (), $ instance = new $ concrete
956
+ );
957
+
958
+ return $ instance ;
927
959
}
928
960
929
961
$ dependencies = $ constructor ->getParameters ();
@@ -941,7 +973,11 @@ public function build($concrete)
941
973
942
974
array_pop ($ this ->buildStack );
943
975
944
- return $ reflector ->newInstanceArgs ($ instances );
976
+ $ this ->fireAfterResolvingAttributeCallbacks (
977
+ $ reflector ->getAttributes (), $ instance = $ reflector ->newInstanceArgs ($ instances )
978
+ );
979
+
980
+ return $ instance ;
945
981
}
946
982
947
983
/**
@@ -966,13 +1002,21 @@ protected function resolveDependencies(array $dependencies)
966
1002
continue ;
967
1003
}
968
1004
1005
+ $ result = null ;
1006
+
1007
+ if (! is_null ($ attribute = $ this ->getContextualAttributeFromDependency ($ dependency ))) {
1008
+ $ result = $ this ->resolveFromAttribute ($ attribute );
1009
+ }
1010
+
969
1011
// If the class is null, it means the dependency is a string or some other
970
1012
// primitive type which we can not resolve since it is not a class and
971
1013
// we will just bomb out with an error since we have no-where to go.
972
- $ result = is_null (Util::getParameterClassName ($ dependency ))
1014
+ $ result ?? = is_null (Util::getParameterClassName ($ dependency ))
973
1015
? $ this ->resolvePrimitive ($ dependency )
974
1016
: $ this ->resolveClass ($ dependency );
975
1017
1018
+ $ this ->fireAfterResolvingAttributeCallbacks ($ dependency ->getAttributes (), $ result );
1019
+
976
1020
if ($ dependency ->isVariadic ()) {
977
1021
$ results = array_merge ($ results , $ result );
978
1022
} else {
@@ -1017,6 +1061,17 @@ protected function getLastParameterOverride()
1017
1061
return count ($ this ->with ) ? end ($ this ->with ) : [];
1018
1062
}
1019
1063
1064
+ /**
1065
+ * Get a contextual attribute from a dependency.
1066
+ *
1067
+ * @param ReflectionParameter $dependency
1068
+ * @return \ReflectionAttribute|null
1069
+ */
1070
+ protected function getContextualAttributeFromDependency ($ dependency )
1071
+ {
1072
+ return $ dependency ->getAttributes (ContextualAttribute::class, ReflectionAttribute::IS_INSTANCEOF )[0 ] ?? null ;
1073
+ }
1074
+
1020
1075
/**
1021
1076
* Resolve a non-class hinted primitive dependency.
1022
1077
*
@@ -1097,6 +1152,29 @@ protected function resolveVariadicClass(ReflectionParameter $parameter)
1097
1152
return array_map (fn ($ abstract ) => $ this ->resolve ($ abstract ), $ concrete );
1098
1153
}
1099
1154
1155
+ /**
1156
+ * Resolve a dependency based on an attribute.
1157
+ *
1158
+ * @param \ReflectionAttribute $attribute
1159
+ * @return mixed
1160
+ */
1161
+ protected function resolveFromAttribute (ReflectionAttribute $ attribute )
1162
+ {
1163
+ $ handler = $ this ->contextualAttributes [$ attribute ->getName ()] ?? null ;
1164
+
1165
+ $ instance = $ attribute ->newInstance ();
1166
+
1167
+ if (is_null ($ handler ) && method_exists ($ instance , 'resolve ' )) {
1168
+ $ handler = $ instance ->resolve (...);
1169
+ }
1170
+
1171
+ if (is_null ($ handler )) {
1172
+ throw new BindingResolutionException ("Contextual binding attribute [ {$ attribute ->getName ()}] has no registered handler. " );
1173
+ }
1174
+
1175
+ return $ handler ($ instance , $ this );
1176
+ }
1177
+
1100
1178
/**
1101
1179
* Throw an exception that the concrete is not instantiable.
1102
1180
*
@@ -1193,6 +1271,18 @@ public function afterResolving($abstract, ?Closure $callback = null)
1193
1271
}
1194
1272
}
1195
1273
1274
+ /**
1275
+ * Register a new after resolving attribute callback for all types.
1276
+ *
1277
+ * @param string $attribute
1278
+ * @param \Closure $callback
1279
+ * @return void
1280
+ */
1281
+ public function afterResolvingAttribute (string $ attribute , \Closure $ callback )
1282
+ {
1283
+ $ this ->afterResolvingAttributeCallbacks [$ attribute ][] = $ callback ;
1284
+ }
1285
+
1196
1286
/**
1197
1287
* Fire all of the before resolving callbacks.
1198
1288
*
@@ -1260,6 +1350,34 @@ protected function fireAfterResolvingCallbacks($abstract, $object)
1260
1350
);
1261
1351
}
1262
1352
1353
+ /**
1354
+ * Fire all of the after resolving attribute callbacks.
1355
+ *
1356
+ * @param \ReflectionAttribute[] $abstract
1357
+ * @param mixed $object
1358
+ * @return void
1359
+ */
1360
+ protected function fireAfterResolvingAttributeCallbacks (array $ attributes , $ object )
1361
+ {
1362
+ foreach ($ attributes as $ attribute ) {
1363
+ if (is_a ($ attribute ->getName (), ContextualAttribute::class, true )) {
1364
+ $ instance = $ attribute ->newInstance ();
1365
+
1366
+ if (method_exists ($ instance , 'after ' )) {
1367
+ $ instance ->after ($ instance , $ object , $ this );
1368
+ }
1369
+ }
1370
+
1371
+ $ callbacks = $ this ->getCallbacksForType (
1372
+ $ attribute ->getName (), $ object , $ this ->afterResolvingAttributeCallbacks
1373
+ );
1374
+
1375
+ foreach ($ callbacks as $ callback ) {
1376
+ $ callback ($ attribute ->newInstance (), $ object , $ this );
1377
+ }
1378
+ }
1379
+ }
1380
+
1263
1381
/**
1264
1382
* Get all callbacks for a given type.
1265
1383
*
0 commit comments