Overview

Namespaces

  • Autarky
    • Config
      • Loaders
    • Console
    • Container
      • Exception
      • Factory
      • Proxy
    • Database
    • Errors
    • Events
    • Files
    • Http
    • Logging
    • Providers
    • Routing
      • Events
    • Testing
    • TwigTemplating
      • Extensions
    • Utils

Classes

  • Autarky\Application
  • Autarky\Config\ConfigProvider
  • Autarky\Config\FileStore
  • Autarky\Config\LoaderFactory
  • Autarky\Config\Loaders\CachingYamlFileLoader
  • Autarky\Config\Loaders\PhpFileLoader
  • Autarky\Config\Loaders\YamlFileLoader
  • Autarky\Console\Application
  • Autarky\Console\Command
  • Autarky\Console\RouteDispatchCommand
  • Autarky\Console\RouteListCommand
  • Autarky\Container\Container
  • Autarky\Container\ContainerProvider
  • Autarky\Container\Factory\AbstractArgument
  • Autarky\Container\Factory\ClassArgument
  • Autarky\Container\Factory\Definition
  • Autarky\Container\Factory\Factory
  • Autarky\Container\Factory\ScalarArgument
  • Autarky\Container\Proxy\AbstractProxy
  • Autarky\Container\Proxy\ProxyProvider
  • Autarky\Database\ConnectionManager
  • Autarky\Database\DatabaseProvider
  • Autarky\Errors\ErrorHandlerManager
  • Autarky\Errors\ErrorHandlerProvider
  • Autarky\Errors\StubErrorHandler
  • Autarky\Events\EventDispatcher
  • Autarky\Events\EventDispatcherProvider
  • Autarky\Files\LockingFilesystem
  • Autarky\Files\PathResolver
  • Autarky\Http\CookieMiddleware
  • Autarky\Http\CookieProvider
  • Autarky\Http\CookieQueue
  • Autarky\Http\SessionMiddleware
  • Autarky\Http\SessionProvider
  • Autarky\Logging\ChannelManager
  • Autarky\Logging\DefaultLogConfigurator
  • Autarky\Logging\LoggingErrorHandler
  • Autarky\Logging\LoggingProvider
  • Autarky\Provider
  • Autarky\Providers\AbstractDependantProvider
  • Autarky\Providers\AbstractProvider
  • Autarky\Routing\Configuration
  • Autarky\Routing\Controller
  • Autarky\Routing\DefaultRouteConfigurator
  • Autarky\Routing\Events\AbstractRouteEvent
  • Autarky\Routing\Events\AfterEvent
  • Autarky\Routing\Events\BeforeEvent
  • Autarky\Routing\Events\RouteMatchedEvent
  • Autarky\Routing\Route
  • Autarky\Routing\RoutePathGenerator
  • Autarky\Routing\Router
  • Autarky\Routing\RoutingProvider
  • Autarky\Routing\UrlGenerator
  • Autarky\Testing\TestCase
  • Autarky\Testing\WebTestCase
  • Autarky\TwigTemplating\Extensions\PartialExtension
  • Autarky\TwigTemplating\Extensions\SessionExtension
  • Autarky\TwigTemplating\Extensions\UrlGenerationExtension
  • Autarky\TwigTemplating\Template
  • Autarky\TwigTemplating\TemplateContext
  • Autarky\TwigTemplating\TemplateEvent
  • Autarky\TwigTemplating\TemplatingEngine
  • Autarky\TwigTemplating\TwigEnvironment
  • Autarky\TwigTemplating\TwigTemplate
  • Autarky\TwigTemplating\TwigTemplatingProvider
  • Autarky\Utils\ArrayUtil

Interfaces

  • Autarky\Config\ConfigInterface
  • Autarky\Config\LoaderInterface
  • Autarky\ConfiguratorInterface
  • Autarky\Container\CallableInvokerInterface
  • Autarky\Container\ClassResolverInterface
  • Autarky\Container\ContainerAwareInterface
  • Autarky\Container\ContainerInterface
  • Autarky\Container\Factory\ArgumentInterface
  • Autarky\Container\Factory\FactoryInterface
  • Autarky\Database\ConnectionFactoryInterface
  • Autarky\Errors\ErrorHandlerInterface
  • Autarky\Errors\ErrorHandlerManagerInterface
  • Autarky\Events\EventDispatcherAwareInterface
  • Autarky\Providers\ConsoleProviderInterface
  • Autarky\Providers\DependantProviderInterface
  • Autarky\Providers\ProviderInterface
  • Autarky\Routing\InvokerInterface
  • Autarky\Routing\RoutePathGeneratorInterface
  • Autarky\Routing\RouterInterface

Traits

  • Autarky\Container\ContainerAwareTrait
  • Autarky\Events\EventDispatcherAwareTrait
  • Autarky\Routing\ControllerTrait

Exceptions

  • Autarky\Config\LoadException
  • Autarky\Container\Exception\ContainerException
  • Autarky\Container\Exception\NotInstantiableException
  • Autarky\Container\Exception\ResolvingException
  • Autarky\Container\Exception\ResolvingInternalException
  • Autarky\Container\Exception\UnresolvableArgumentException
  • Autarky\Database\CannotConnectException
  • Autarky\Files\IOException
  • Autarky\Providers\ProviderException
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /**
  3:  * This file is part of the Autarky package.
  4:  *
  5:  * (c) Andreas Lutro <anlutro@gmail.com>
  6:  *
  7:  * For the full copyright and license information, please view the LICENSE
  8:  * file that was distributed with this source code.
  9:  */
 10: 
 11: namespace Autarky\Container;
 12: 
 13: use Autarky\Container\Factory\Definition;
 14: use Autarky\Container\Factory\FactoryInterface;
 15: 
 16: use ReflectionClass;
 17: use ReflectionException;
 18: use ReflectionFunction;
 19: use ReflectionFunctionAbstract;
 20: use ReflectionMethod;
 21: use ReflectionParameter;
 22: 
 23: /**
 24:  * Default implementation of the container.
 25:  */
 26: class Container implements ContainerInterface
 27: {
 28:     /**
 29:      * Resolved instances.
 30:      *
 31:      * @var array
 32:      */
 33:     protected $instances = [];
 34: 
 35:     /**
 36:      * Factories.
 37:      *
 38:      * @var array
 39:      */
 40:     protected $factories = [];
 41: 
 42:     /**
 43:      * Classes that should be shared instances.
 44:      *
 45:      * @var array
 46:      */
 47:     protected $shared = [];
 48: 
 49:     /**
 50:      * Aliases.
 51:      *
 52:      * @var array
 53:      */
 54:     protected $aliases = [];
 55: 
 56:     /**
 57:      * Parameter specifications.
 58:      *
 59:      * @var array
 60:      */
 61:     protected $params = [];
 62: 
 63:     /**
 64:      * Resolving callbacks.
 65:      *
 66:      * @var array
 67:      */
 68:     protected $resolvingCallbacks = [];
 69: 
 70:     /**
 71:      * More resolving callbacks.
 72:      *
 73:      * @var array
 74:      */
 75:     protected $resolvingAnyCallbacks = [];
 76: 
 77:     /**
 78:      * Internal classes.
 79:      *
 80:      * @var array
 81:      */
 82:     protected $internals = [];
 83: 
 84:     /**
 85:      * Whether internal classes should be protected from resolving or not.
 86:      *
 87:      * @var boolean
 88:      */
 89:     protected $protectInternals = true;
 90: 
 91:     /**
 92:      * Whether to "autowire" classes or not.
 93:      *
 94:      * @var boolean
 95:      */
 96:     protected $autowire = true;
 97: 
 98:     /**
 99:      * Create a new instance of the container.
100:      *
101:      * On instantiation, the container instance will bind itself onto itself,
102:      * and alias the ContainerInterface to the class name.
103:      */
104:     public function __construct()
105:     {
106:         $this->instance('Autarky\Container\Container', $this);
107:         $this->alias('Autarky\Container\Container', 'Autarky\Container\ContainerInterface');
108:         $this->alias('Autarky\Container\Container', 'Autarky\Container\ClassResolverInterface');
109:         $this->alias('Autarky\Container\Container', 'Autarky\Container\CallableInvokerInterface');
110:     }
111: 
112:     /**
113:      * {@inheritdoc}
114:      */
115:     public function define($class, $factory, array $params = array())
116:     {
117:         if ($params) {
118:             $this->params($class, $params);
119:         }
120: 
121:         if (!$factory instanceof FactoryInterface) {
122:             $factory = Definition::getDefaultForCallable($factory);
123:         }
124: 
125:         return $this->factories[$class] = $factory;
126:     }
127: 
128:     /**
129:      * {@inheritdoc}
130:      */
131:     public function invoke($callable, array $params = array())
132:     {
133:         // if $callable is a string, assume it's a class name with the method
134:         // "invoke" defined
135:         if (is_string($callable) && !is_callable($callable)) {
136:             $callable = [$callable, 'invoke'];
137:         }
138: 
139:         // if $callable is a static method-like string, convert it to an array
140:         if (is_string($callable) && strpos($callable, '::') !== false) {
141:             $callable = explode('::', $callable);
142:         }
143: 
144:         $class = null;
145:         $object = null;
146: 
147:         if (is_array($callable)) {
148:             $class = $callable[0];
149:             $method = $callable[1];
150: 
151:             if (is_object($class)) {
152:                 $object = $class;
153:                 $class = get_class($object);
154:             } else {
155:                 $object = $this->resolve($class);
156:             }
157: 
158:             $reflFunc = new ReflectionMethod($object, $method);
159: 
160:             if ($reflFunc->isStatic()) {
161:                 $object = null;
162:             }
163: 
164:             $callableString = $class.'::'.$method;
165:             if (isset($this->params[$callableString])) {
166:                 $params = array_replace($this->params[$callableString], $params);
167:             }
168:         } else if (is_callable($callable)) {
169:             $reflFunc = new ReflectionFunction($callable);
170:         } else {
171:             $type = is_object($callable) ? get_class($callable) : gettype($callable);
172:             throw new \InvalidArgumentException("Callable must be a callable or array, $type given");
173:         }
174: 
175:         $args = $this->getFunctionArguments($reflFunc, $params);
176: 
177:         if ($class) {
178:             return $reflFunc->invokeArgs($object, $args);
179:         }
180: 
181:         return $reflFunc->invokeArgs($args);
182:     }
183: 
184:     /**
185:      * {@inheritdoc}
186:      */
187:     public function resolve($class, array $params = array())
188:     {
189:         $alias = null;
190: 
191:         if (isset($this->aliases[$class])) {
192:             $alias = $class;
193:             $class = $this->aliases[$class];
194:         }
195: 
196:         // will throw an exception if the class or alias is protected
197:         $this->checkProtected($class, $alias);
198: 
199:         // if the class is shared, an instance may already exist
200:         if (isset($this->instances[$class])) {
201:             return $this->instances[$class];
202:         }
203: 
204:         if (isset($this->params[$class])) {
205:             $params = array_replace($this->params[$class], $params);
206:         }
207: 
208:         // internal classes don't need to be protected when resolving
209:         // dependencies. save the previous protectInternals value, it will be
210:         // reset after resolving dependencies
211:         $previousState = $this->protectInternals;
212:         $this->protectInternals = false;
213: 
214:         // if no factory is defined for the class, create one
215:         if (!isset($this->factories[$class]) && $this->autowire) {
216:             $this->factories[$class] = Definition::getDefaultForClass($class);
217:         }
218: 
219:         if (!isset($this->factories[$class])) {
220:             if ($alias) {
221:                 $class = "$class (via $alias)";
222:             }
223:             throw new Exception\ResolvingException("No factory defined for $class");
224:         }
225: 
226:         $object = $this->callFactory($this->factories[$class], $params);
227: 
228:         $this->protectInternals = $previousState;
229: 
230:         if ($object instanceof ContainerAwareInterface) {
231:             $object->setContainer($this);
232:         }
233: 
234:         if ($alias) {
235:             $this->callResolvingCallbacks($alias, $object);
236:         }
237:         $this->callResolvingCallbacks($class, $object);
238: 
239:         if ($this->isShared($class)) {
240:             $this->instances[$class] = $object;
241:         }
242: 
243:         return $object;
244:     }
245: 
246:     /**
247:      * Call a factory.
248:      *
249:      * @param  Factory\FactoryInterface  $factory
250:      * @param  array  $params
251:      *
252:      * @return object
253:      */
254:     protected function callFactory(Factory\FactoryInterface $factory, array $params = array())
255:     {
256:         return $factory->invoke($this, $params);
257:     }
258: 
259:     /**
260:      * Make a new factory definition for a class.
261:      *
262:      * @param  callable $callable
263:      * @param  boolean  $reflect  Optional, default false - Whether to use reflection to find the arguments to the callable. If false, you will need to add arguments to the definition object yourself
264:      *
265:      * @return Definition
266:      */
267:     public function makeFactory($callable, $reflect = false)
268:     {
269:         if ($reflect) {
270:             return Definition::getFromReflection($callable, null);
271:         }
272: 
273:         return new Definition($callable);
274:     }
275: 
276:     /**
277:      * Get the existing factory for a class. If a factory is not already defined
278:      * a default one will be created via reflection.
279:      *
280:      * @param  string $class  Name of the class
281:      * @param  array  $params Optional
282:      *
283:      * @return FactoryInterface
284:      */
285:     public function getFactory($class, array $params = array())
286:     {
287:         if (!isset($this->factories[$class]) && $this->autowire) {
288:             $this->factories[$class] = Definition::getDefaultForClass($class);
289:         }
290: 
291:         $factory = $this->factories[$class];
292: 
293:         // if $params is defined, we need to either make a copy of the existing
294:         // Factory or make the Definition create a new factory with the params
295:         if ($params) {
296:             $factory = $factory->getFactory($params);
297:         }
298: 
299:         return $factory;
300:     }
301: 
302:     /**
303:      * Get an array of arguments to a function, resolving type-hinted arguments
304:      * automatically on the way.
305:      *
306:      * @param  ReflectionFunctionAbstract $func
307:      * @param  array                      $params
308:      *
309:      * @return array
310:      *
311:      * @throws Exception\UnresolvableArgumentException If any of the arguments are not type-hinted, does not have a default value and is not specified in $params
312:      */
313:     protected function getFunctionArguments(ReflectionFunctionAbstract $func, array $params = array())
314:     {
315:         $args = [];
316: 
317:         foreach ($func->getParameters() as $param) {
318:             $class = $param->getClass();
319: 
320:             if ($class) {
321:                 $args[] = $this->resolveClassArg($class, $param, $params);
322:             } else {
323:                 $args[] = $this->resolveNonClassArg($param, $params, $func);
324:             }
325:         }
326: 
327:         return $args;
328:     }
329: 
330:     /**
331:      * Resolve a class type-hinted argument for a funtion.
332:      *
333:      * @param  ReflectionClass     $class
334:      * @param  ReflectionParameter $param
335:      * @param  array               $params
336:      *
337:      * @return object|null
338:      */
339:     protected function resolveClassArg(ReflectionClass $class, ReflectionParameter $param, array $params)
340:     {
341:         $name = '$'.$param->getName();
342:         $class = $class->getName();
343: 
344:         // loop to prevent code repetition. executes once trying to find the
345:         // parameter name in the $params array, then once more trying to find
346:         // the class name (typehint) of the parameter.
347:         while ($name !== null) {
348:             if ($params && array_key_exists($name, $params)) {
349:                 $class = $params[$name];
350:             }
351: 
352:             if ($class instanceof Factory\FactoryInterface) {
353:                 return $class->invoke($this);
354:             }
355: 
356:             if (is_object($class)) {
357:                 return $class;
358:             }
359: 
360:             $name = ($name != $class) ? $class : null;
361:         }
362: 
363:         try {
364:             return $this->resolve($class);
365:         } catch (ReflectionException $exception) {
366:             if ($param->isOptional()) {
367:                 return null;
368:             }
369: 
370:             throw $exception;
371:         }
372:     }
373: 
374:     /**
375:      * Resolve a non-class function argument.
376:      *
377:      * @param  ReflectionParameter        $param
378:      * @param  array                      $params
379:      * @param  ReflectionFunctionAbstract $func
380:      *
381:      * @return mixed
382:      */
383:     protected function resolveNonClassArg(ReflectionParameter $param, array $params, ReflectionFunctionAbstract $func)
384:     {
385:         $name = '$'.$param->getName();
386: 
387:         if ($params && array_key_exists($name, $params)) {
388:             $argument = $params[$name];
389: 
390:             if (is_array($argument) && isset($this->factories[$argument[0]])) {
391:                 $argument = $this->callFactory($argument[0], $argument[1]);
392:             }
393: 
394:             return $argument;
395:         }
396: 
397:         if ($param->isDefaultValueAvailable()) {
398:             return $param->getDefaultValue();
399:         }
400: 
401:         throw Exception\UnresolvableArgumentException::fromReflectionParam($param, $func);
402:     }
403: 
404:     /**
405:      * {@inheritdoc}
406:      */
407:     public function resolving($classOrClasses, callable $callback)
408:     {
409:         foreach ((array) $classOrClasses as $class) {
410:             $this->resolvingCallbacks[$class][] = $callback;
411:         }
412:     }
413: 
414:     /**
415:      * {@inheritdoc}
416:      */
417:     public function resolvingAny(callable $callback)
418:     {
419:         $this->resolvingAnyCallbacks[] = $callback;
420:     }
421: 
422:     /**
423:      * Call resolving callbacks for an object.
424:      *
425:      * @param  string $key    Container key - usually the class name
426:      * @param  object $object
427:      *
428:      * @return void
429:      */
430:     protected function callResolvingCallbacks($key, $object)
431:     {
432:         foreach ($this->resolvingAnyCallbacks as $callback) {
433:             call_user_func($callback, $object, $this);
434:         }
435: 
436:         if (isset($this->resolvingCallbacks[$key])) {
437:             foreach ($this->resolvingCallbacks[$key] as $callback) {
438:                 call_user_func($callback, $object, $this);
439:             }
440:         }
441:     }
442: 
443:     /**
444:      * Enable or disable autowiring.
445:      *
446:      * @param boolean $autowire
447:      */
448:     public function setAutowire($autowire)
449:     {
450:         $this->autowire = (bool) $autowire;
451:     }
452: 
453:     /**
454:      * {@inheritdoc}
455:      */
456:     public function isBound($class)
457:     {
458:         if (isset($this->aliases[$class])) {
459:             $class = $this->aliases[$class];
460:         }
461: 
462:         return isset($this->instances[$class])
463:             || isset($this->factories[$class])
464:             || isset($this->shared[$class]);
465:     }
466: 
467:     /**
468:      * {@inheritdoc}
469:      */
470:     public function instance($class, $instance)
471:     {
472:         $this->shared[$class] = true;
473:         $this->instances[$class] = $instance;
474:     }
475: 
476:     /**
477:      * {@inheritdoc}
478:      */
479:     public function share($classOrClasses)
480:     {
481:         foreach ((array) $classOrClasses as $class) {
482:             $this->shared[$class] = true;
483:         }
484:     }
485: 
486:     /**
487:      * {@inheritdoc}
488:      */
489:     public function internal($classOrClasses)
490:     {
491:         foreach ((array) $classOrClasses as $class) {
492:             $this->internals[$class] = true;
493:         }
494:     }
495: 
496:     /**
497:      * {@inheritdoc}
498:      */
499:     public function alias($original, $aliasOrAliases)
500:     {
501:         foreach ((array) $aliasOrAliases as $alias) {
502:             $this->aliases[$alias] = $original;
503:         }
504:     }
505: 
506:     /**
507:      * {@inheritdoc}
508:      */
509:     public function params($keys, array $params)
510:     {
511:         foreach ((array) $keys as $key) {
512:             if (is_array($key)) {
513:                 $key = $key[0].'::'.$key[1];
514:             }
515: 
516:             if (!isset($this->params[$key])) {
517:                 $this->params[$key] = $params;
518:             } else {
519:                 $this->params[$key] = array_replace($this->params[$key], $params);
520:             }
521:         }
522:     }
523: 
524:     /**
525:      * Determine if a class is shared or not.
526:      *
527:      * @param  string  $class
528:      *
529:      * @return boolean
530:      */
531:     protected function isShared($class)
532:     {
533:         return isset($this->shared[$class]) && $this->shared[$class];
534:     }
535: 
536:     /**
537:      * Check if a class and its alias (optionally) are protected, and throw an
538:      * exception if they are.
539:      *
540:      * @param  string $class
541:      * @param  string|null $alias
542:      *
543:      * @return void
544:      *
545:      * @throws Exception\ResolvingInternalException If class or alias is internal
546:      */
547:     protected function checkProtected($class, $alias)
548:     {
549:         if (!$this->protectInternals) {
550:             return;
551:         }
552: 
553:         if ($this->isProtected($class) || ($alias && $this->isProtected($alias))) {
554:             if ($alias) {
555:                 $class = "$class (via alias $alias)";
556:             }
557:             $msg = "Class $class is internal and cannot be resolved.";
558:             throw new Exception\ResolvingInternalException($msg);
559:         }
560:     }
561: 
562:     /**
563:      * Determine if a class is protected or not.
564:      *
565:      * @param  string  $class
566:      *
567:      * @return boolean
568:      */
569:     protected function isProtected($class)
570:     {
571:         return isset($this->internals[$class]) && $this->internals[$class];
572:     }
573: }
574: 
Autarky Framework API documentation generated by ApiGen