1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace Autarky\Container\Factory;
12:
13: use Autarky\Container\ContainerInterface;
14: use Autarky\Container\Exception\NotInstantiableException;
15: use Autarky\Container\Exception\UnresolvableArgumentException;
16: use Closure;
17: use ReflectionClass;
18: use ReflectionException;
19: use ReflectionFunction;
20: use ReflectionFunctionAbstract;
21: use ReflectionMethod;
22:
23: 24: 25:
26: class Definition implements FactoryInterface
27: {
28: 29: 30: 31: 32:
33: protected $callable;
34:
35: 36: 37: 38: 39:
40: protected $name;
41:
42: 43: 44: 45: 46:
47: protected $argumentPositions = [];
48:
49: 50: 51: 52: 53:
54: protected $argumentNames = [];
55:
56: 57: 58: 59: 60:
61: protected $argumentClasses = [];
62:
63: 64: 65: 66: 67:
68: protected $argumentPosition = 0;
69:
70: 71: 72: 73: 74:
75: public function __construct($callable, $name = null)
76: {
77: $this->callable = $callable;
78: $this->name = $name;
79: }
80:
81: 82: 83: 84: 85: 86: 87: 88:
89: public static function getDefaultForClass($class, array $params = array())
90: {
91: $reflectionClass = new ReflectionClass($class);
92:
93: if (!$reflectionClass->isInstantiable()) {
94: throw new NotInstantiableException("Class $class is not instantiable");
95: }
96:
97: $factory = new static([$reflectionClass, 'newInstance'], "$class::__construct");
98:
99: if ($reflectionClass->hasMethod('__construct')) {
100: static::addReflectionArguments($factory, $reflectionClass->getMethod('__construct'));
101: }
102:
103: return $factory->getFactory($params);
104: }
105:
106: 107: 108: 109: 110: 111: 112: 113:
114: public static function getDefaultForCallable($callable, array $params = array())
115: {
116: if ($callable instanceof Closure) {
117: $factory = new static($callable, 'closure');
118: $factory->addOptionalClassArgument('$container', 'Autarky\Container\ContainerInterface');
119: return $factory->getFactory($params);
120: }
121:
122: return static::getFromReflection($callable, null)
123: ->getFactory($params);
124: }
125:
126: 127: 128: 129: 130: 131: 132: 133:
134: public static function getFromReflection($callable, ReflectionFunctionAbstract $reflectionFunction = null)
135: {
136: $factory = new static($callable);
137:
138: static::addReflectionArguments($factory, $reflectionFunction);
139:
140: return $factory;
141: }
142:
143: 144: 145: 146: 147: 148:
149: protected static function addReflectionArguments(Definition $factory, ReflectionFunctionAbstract $reflectionFunction = null)
150: {
151: if (!$reflectionFunction) {
152: $callable = $factory->getCallable();
153:
154: if (is_array($callable)) {
155: $reflectionFunction = new ReflectionMethod($callable[0], $callable[1]);
156: } else {
157: $reflectionFunction = new ReflectionFunction($callable);
158: }
159: }
160:
161: foreach ($reflectionFunction->getParameters() as $arg) {
162: try {
163: $name = $arg->getName();
164: $required = ! $arg->isOptional();
165: if ($argClass = $arg->getClass()) {
166: $factory->addClassArgument($name, $argClass->getName(), $required);
167: } else {
168: $default = ($required ? null : $arg->getDefaultValue());
169: $factory->addScalarArgument($name, null, $required, $default);
170: }
171: } catch (ReflectionException $re) {
172: throw UnresolvableArgumentException::fromReflectionParam(
173: $arg, $reflectionFunction, $re);
174: }
175: }
176: }
177:
178: 179: 180: 181: 182:
183: public function getName()
184: {
185: return $this->name;
186: }
187:
188: 189: 190: 191: 192:
193: public function getCallable()
194: {
195: return $this->callable;
196: }
197:
198: 199: 200: 201: 202:
203: public function getArguments()
204: {
205: return $this->argumentPositions;
206: }
207:
208: 209: 210: 211: 212: 213: 214: 215:
216: public function addScalarArgument($name, $type, $required = true, $default = null)
217: {
218: return $this->addArgument(new ScalarArgument($this->argumentPosition++, $name, $type, $required, $default));
219: }
220:
221: 222: 223: 224: 225: 226: 227:
228: public function addOptionalScalarArgument($name, $type, $default)
229: {
230: return $this->addArgument(new ScalarArgument($this->argumentPosition++, $name, $type, false, $default));
231: }
232:
233: 234: 235: 236: 237: 238: 239:
240: public function addClassArgument($name, $class, $required = true)
241: {
242: return $this->addArgument(new ClassArgument($this->argumentPosition++, $name, $class, $required));
243: }
244:
245: 246: 247: 248: 249: 250:
251: public function addOptionalClassArgument($name, $class)
252: {
253: return $this->addArgument(new ClassArgument($this->argumentPosition++, $name, $class, false));
254: }
255:
256: 257: 258: 259: 260:
261: public function addArgument(ArgumentInterface $argument)
262: {
263: $this->argumentPositions[$argument->getPosition()] = $argument;
264: $this->argumentNames[$argument->getName()] = $argument;
265: if ($argument->isClass()) {
266: $this->argumentClasses[$argument->getClass()] = $argument;
267: }
268: return $argument;
269: }
270:
271: 272: 273: 274: 275: 276: 277:
278: public function getFactory(array $params = array())
279: {
280: return new Factory($this, $params);
281: }
282:
283: 284: 285:
286: public function invoke(ContainerInterface $container, array $params = array())
287: {
288: return $this->getFactory($params)
289: ->invoke($container);
290: }
291:
292: 293: 294: 295: 296: 297: 298: 299:
300: public function __invoke(ContainerInterface $container, array $params = array())
301: {
302: return $this->invoke($container, $params);
303: }
304: }
305: