1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace Autarky;
12:
13: use Closure;
14: use SplPriorityQueue;
15: use SplDoublyLinkedList;
16: use Stack\Builder as StackBuilder;
17:
18: use Symfony\Component\HttpFoundation\Request;
19: use Symfony\Component\HttpFoundation\RequestStack;
20: use Symfony\Component\HttpKernel\HttpKernelInterface;
21:
22: use Autarky\Config\ConfigInterface;
23: use Autarky\Console\Application as ConsoleApplication;
24: use Autarky\Container\ContainerInterface;
25: use Autarky\Errors\ErrorHandlerManagerInterface;
26: use Autarky\Http\Kernel;
27: use Autarky\Providers\ProviderInterface;
28: use Autarky\Providers\DependantProviderInterface;
29: use Autarky\Providers\ConsoleProviderInterface;
30: use Autarky\Providers\AbstractProvider;
31:
32: 33: 34:
35: class Application implements HttpKernelInterface
36: {
37: 38: 39:
40: const VERSION = '0.10.0';
41:
42: 43: 44: 45: 46:
47: protected $providers = [];
48:
49: 50: 51:
52: protected $middlewares;
53:
54: 55: 56:
57: protected $kernel;
58:
59: 60: 61:
62: protected $stack;
63:
64: 65: 66:
67: protected $config;
68:
69: 70: 71:
72: protected $container;
73:
74: 75: 76:
77: protected $errorHandler;
78:
79: 80: 81:
82: protected $console;
83:
84: 85: 86:
87: protected $environment;
88:
89: 90: 91:
92: protected $booting = false;
93:
94: 95: 96:
97: protected $booted = false;
98:
99: 100: 101:
102: protected $configurators;
103:
104: 105: 106:
107: protected $requests;
108:
109: 110: 111: 112: 113: 114:
115: public function __construct($environment, array $providers)
116: {
117: $this->middlewares = new SplPriorityQueue;
118: $this->configurators = new SplDoublyLinkedList;
119: $this->requests = new RequestStack;
120: $this->setEnvironment($environment);
121:
122: foreach ($providers as $provider) {
123: $class = is_object($provider) ? get_class($provider) : (string) $provider;
124: $this->providers[$class] = $provider;
125: }
126: }
127:
128: 129: 130: 131: 132: 133: 134: 135: 136:
137: public function config($configurator)
138: {
139: if ($this->booted) {
140: $this->invokeConfigurator($configurator);
141: } else {
142: $this->configurators->push($configurator);
143: }
144: }
145:
146: 147: 148: 149: 150: 151: 152:
153: protected function invokeConfigurator($configurator)
154: {
155: if (is_callable($configurator)) {
156: call_user_func($configurator, $this);
157: return;
158: }
159:
160: if (is_string($configurator)) {
161: $configurator = $this->container->resolve($configurator);
162: }
163:
164: if ($configurator instanceof ConfiguratorInterface) {
165: $configurator->configure();
166: } else {
167: throw new \UnexpectedValueException('Invalid configurator');
168: }
169: }
170:
171: 172: 173: 174: 175:
176: public function setEnvironment($environment)
177: {
178: if ($this->booting) {
179: throw new \RuntimeException('Cannot set environment after application has booted');
180: }
181:
182: if ($environment instanceof Closure) {
183: $environment = call_user_func($environment);
184: }
185:
186: if (!is_string($environment)) {
187: throw new \InvalidArgumentException('Environment must be a string');
188: }
189:
190: $this->environment = $environment;
191: }
192:
193: 194: 195: 196: 197:
198: public function getEnvironment()
199: {
200: if (!$this->booting) {
201: throw new \RuntimeException('Environment has not yet been resolved');
202: }
203:
204: return $this->environment;
205: }
206:
207: 208: 209: 210: 211:
212: public function getProviders()
213: {
214: return array_keys($this->providers);
215: }
216:
217: 218: 219: 220: 221:
222: public function setErrorHandler(ErrorHandlerManagerInterface $errorHandler)
223: {
224: $this->errorHandler = $errorHandler;
225: }
226:
227: 228: 229: 230: 231:
232: public function getErrorHandler()
233: {
234: return $this->errorHandler;
235: }
236:
237: 238: 239: 240: 241:
242: public function setContainer(ContainerInterface $container)
243: {
244: $this->container = $container;
245: $container->instance('Autarky\Application', $this);
246: $container->instance('Symfony\Component\HttpFoundation\RequestStack', $this->requests);
247: }
248:
249: 250: 251: 252: 253:
254: public function getContainer()
255: {
256: return $this->container;
257: }
258:
259: 260: 261: 262: 263:
264: public function setConfig(ConfigInterface $config)
265: {
266: $this->config = $config;
267: }
268:
269: 270: 271: 272: 273:
274: public function getConfig()
275: {
276: return $this->config;
277: }
278:
279: 280: 281: 282: 283:
284: public function getRouter()
285: {
286: return $this->container->resolve('Autarky\Routing\RouterInterface');
287: }
288:
289: 290: 291: 292: 293:
294: public function getRequestStack()
295: {
296: return $this->requests;
297: }
298:
299: 300: 301: 302: 303: 304:
305: public function addMiddleware($middleware, $priority = null)
306: {
307: $this->middlewares->insert($middleware, (int) $priority);
308: }
309:
310: 311: 312: 313: 314:
315: public function bootConsole()
316: {
317: $this->console = new ConsoleApplication('Autarky', static::VERSION);
318:
319: $this->console->setAutarkyApplication($this);
320:
321: $this->boot();
322:
323: return $this->console;
324: }
325:
326: 327: 328: 329: 330:
331: public function boot()
332: {
333: if ($this->booted) return;
334:
335: $this->booting = true;
336:
337: $this->registerProviders();
338:
339: foreach ($this->configurators as $configurator) {
340: $this->invokeConfigurator($configurator);
341: }
342:
343: $this->resolveStack();
344:
345: $this->booted = true;
346: }
347:
348: 349: 350: 351: 352:
353: protected function registerProviders()
354: {
355: $dependants = [];
356:
357: foreach ($this->providers as $provider) {
358: if (is_string($provider)) {
359: $provider = new $provider();
360: }
361:
362: $this->registerProvider($provider);
363:
364: if ($provider instanceof DependantProviderInterface) {
365: $dependants[] = $provider;
366: }
367: }
368:
369: foreach ($dependants as $dependant) {
370: $this->checkProviderDependencies($dependant);
371: }
372: }
373:
374: 375: 376: 377: 378: 379: 380:
381: protected function registerProvider(ProviderInterface $provider)
382: {
383: if ($provider instanceof AbstractProvider) {
384: $provider->setApplication($this);
385: }
386:
387: $provider->register();
388:
389: if ($this->console && $provider instanceof ConsoleProviderInterface) {
390: $provider->registerConsole($this->console);
391: }
392: }
393:
394: protected function checkProviderDependencies(DependantProviderInterface $provider)
395: {
396: $errors = [];
397:
398: foreach ($provider->getClassDependencies() as $class) {
399: if (!class_exists($class)) {
400: $errors[] = "Class must exist: $class";
401: }
402: }
403:
404: foreach ($provider->getContainerDependencies() as $class) {
405: if (!$this->container->isBound($class)) {
406: $errors[] = "Class must be bound to the container: $class";
407: }
408: }
409:
410: foreach ($provider->getProviderDependencies() as $class) {
411: if (!isset($this->providers[$class])) {
412: $errors[] = "Provider must be loaded: $class";
413: }
414: }
415:
416: if ($errors) {
417: $providerClass = get_class($provider);
418: $message = "Errors while registering provider: $providerClass";
419:
420: if (count($errors) > 1) {
421: $message .= "\n".implode("\n", $errors);
422: } else {
423: $message .= " - {$errors[0]}";
424: }
425:
426: throw new Providers\ProviderException($message, $errors);
427: }
428: }
429:
430: 431: 432: 433: 434:
435: protected function resolveStack()
436: {
437: if ($this->stack !== null) {
438: return $this->stack;
439: }
440:
441: $this->stack = new StackBuilder;
442:
443: foreach ($this->middlewares as $middleware) {
444: call_user_func_array([$this->stack, 'push'], (array) $middleware);
445: }
446:
447: return $this->stack;
448: }
449:
450: 451: 452: 453: 454:
455: protected function resolveKernel()
456: {
457: if ($this->kernel !== null) {
458: return $this->kernel;
459: }
460:
461: $class = 'Symfony\Component\EventDispatcher\EventDispatcherInterface';
462: $eventDispatcher = $this->container->isBound($class) ?
463: $this->container->resolve($class) : null;
464:
465: $kernel = new Kernel(
466: $this->getRouter(),
467: $this->requests,
468: $this->errorHandler,
469: $eventDispatcher
470: );
471:
472: return $this->kernel = $this->resolveStack()
473: ->resolve($kernel);
474: }
475:
476: 477: 478: 479: 480: 481: 482: 483:
484: public function run(Request $request = null, $send = true)
485: {
486: if ($request === null) {
487: $request = Request::createFromGlobals();
488: }
489:
490: $response = $this->handle($request);
491:
492: return $send ? $response->send() : $response;
493: }
494:
495: 496: 497:
498: public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $throw = false)
499: {
500: $this->boot();
501:
502: return $this->resolveKernel()
503: ->handle($request, $type, $throw);
504: }
505:
506: 507: 508:
509: public function resolve()
510: {
511: return call_user_func_array([$this->container, 'resolve'], func_get_args());
512: }
513:
514: 515: 516:
517: public function define()
518: {
519: return call_user_func_array([$this->container, 'define'], func_get_args());
520: }
521:
522: 523: 524:
525: public function share()
526: {
527: return call_user_func_array([$this->container, 'share'], func_get_args());
528: }
529:
530: 531: 532:
533: public function alias()
534: {
535: return call_user_func_array([$this->container, 'alias'], func_get_args());
536: }
537:
538: 539: 540:
541: public function params()
542: {
543: return call_user_func_array([$this->container, 'params'], func_get_args());
544: }
545:
546: 547: 548:
549: public function invoke()
550: {
551: return call_user_func_array([$this->container, 'invoke'], func_get_args());
552: }
553:
554: 555: 556:
557: public function route()
558: {
559: return call_user_func_array([$this->getRouter(), 'addRoute'], func_get_args());
560: }
561: }
562: