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\Errors;
 12: 
 13: use Exception;
 14: use ErrorException;
 15: use ReflectionFunction;
 16: use ReflectionMethod;
 17: use SplDoublyLinkedList;
 18: use Symfony\Component\HttpFoundation\Response;
 19: use Symfony\Component\Debug\Exception\FatalErrorException;
 20: use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 21: 
 22: /**
 23:  * Manager that can handle exceptions as well as keep track of multiple other
 24:  * exception handlers.
 25:  */
 26: class ErrorHandlerManager implements ErrorHandlerManagerInterface
 27: {
 28:     /**
 29:      * @var \Autarky\Errors\HandlerResolver
 30:      */
 31:     protected $resolver;
 32: 
 33:     /**
 34:      * @var \SplDoublyLinkedList
 35:      */
 36:     protected $handlers;
 37: 
 38:     /**
 39:      * @var ErrorHandlerInterface|null
 40:      */
 41:     protected $defaultHandler;
 42: 
 43:     /**
 44:      * Re-throw exceptions rather than handling them.
 45:      *
 46:      * @var boolean
 47:      */
 48:     protected $rethrow = false;
 49: 
 50:     /**
 51:      * @param HandlerResolver           $resolver
 52:      */
 53:     public function __construct(HandlerResolver $resolver)
 54:     {
 55:         $this->resolver = $resolver;
 56:         $this->handlers = new SplDoublyLinkedList;
 57:     }
 58: 
 59:     /**
 60:      * {@inheritdoc}
 61:      */
 62:     public function setRethrow($rethrow)
 63:     {
 64:         if ($rethrow !== null) {
 65:             $this->rethrow = (bool) $rethrow;
 66:         } else if (PHP_SAPI === 'cli') {
 67:             $this->rethrow = true;
 68:         } else {
 69:             $this->rethrow = false;
 70:         }
 71:     }
 72: 
 73:     /**
 74:      * {@inheritdoc}
 75:      */
 76:     public function appendHandler($handler)
 77:     {
 78:         $this->checkHandler($handler);
 79:         $this->handlers->push($handler);
 80:     }
 81: 
 82:     /**
 83:      * {@inheritdoc}
 84:      */
 85:     public function prependHandler($handler)
 86:     {
 87:         $this->checkHandler($handler);
 88:         $this->handlers->unshift($handler);
 89:     }
 90: 
 91:     protected function checkHandler($handler)
 92:     {
 93:         if (
 94:             !$handler instanceof ErrorHandlerInterface
 95:             && !is_callable($handler)
 96:             && !is_string($handler)
 97:         ) {
 98:             $type = is_object($handler) ? get_class($handler) : gettype($handler);
 99:             throw new \InvalidArgumentException("Error handler must be callable, string or instance of Autarky\Errors\ErrorHandlerInterface, $type given");
100:         }
101:     }
102: 
103:     /**
104:      * {@inheritdoc}
105:      */
106:     public function setDefaultHandler(ErrorHandlerInterface $handler)
107:     {
108:         $this->defaultHandler = $handler;
109:     }
110: 
111:     /**
112:      * {@inheritdoc}
113:      */
114:     public function register()
115:     {
116:         set_error_handler([$this, 'handleError']);
117: 
118:         if (!$this->rethrow) {
119:             set_exception_handler([$this, 'handleUncaught']);
120:             register_shutdown_function([$this, 'handleShutdown']);
121:         } else {
122:             register_shutdown_function([$this, 'throwFatalErrorException']);
123:         }
124:     }
125: 
126:     /**
127:      * {@inheritdoc}
128:      */
129:     public function handle(Exception $exception)
130:     {
131:         if ($this->rethrow) throw $exception;
132: 
133:         foreach ($this->handlers as $index => $handler) {
134:             try {
135:                 if (is_string($handler)) {
136:                     $handler = $this->resolver->resolve($handler);
137:                     $this->handlers->offsetSet($index, $handler);
138:                 } else if (is_array($handler) && is_string($handler[0])) {
139:                     $handler[0] = $this->resolver->resolve($handler[0]);
140:                     $this->handlers->offsetSet($index, $handler);
141:                 }
142: 
143:                 if (!$this->matchesTypehint($handler, $exception)) {
144:                     continue;
145:                 }
146: 
147:                 $result = $this->callHandler($handler, $exception);
148: 
149:                 if ($result !== null) {
150:                     return $this->makeResponse($result, $exception);
151:                 }
152:             } catch (Exception $newException) {
153:                 return $this->handle($newException);
154:             }
155:         }
156: 
157:         return $this->makeResponse($this->defaultHandler->handle($exception), $exception);
158:     }
159: 
160:     /**
161:      * Transform an exception handler's response into a Response object.
162:      *
163:      * @param  mixed      $response
164:      * @param  \Exception $exception
165:      *
166:      * @return \Symfony\Component\HttpFoundation\Response
167:      */
168:     protected function makeResponse($response, Exception $exception)
169:     {
170:         if (!$response instanceof Response) {
171:             $response = new Response($response);
172:         }
173: 
174:         if (!$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {
175:             if ($exception instanceof HttpExceptionInterface) {
176:                 $response->setStatusCode($exception->getStatusCode());
177:                 $response->headers->add($exception->getHeaders());
178:             } else {
179:                 $response->setStatusCode(500);
180:             }
181:         }
182: 
183:         return $response;
184:     }
185: 
186:     /**
187:      * Check if a handler's argument typehint matches an exception.
188:      *
189:      * @param  callable|ErrorHandlerInterface $handler
190:      * @param  \Exception                     $exception
191:      *
192:      * @return bool
193:      */
194:     protected function matchesTypehint($handler, Exception $exception)
195:     {
196:         if ($handler instanceof ErrorHandlerInterface) {
197:             return true;
198:         }
199: 
200:         if (is_array($handler)) {
201:             $reflection = (new ReflectionMethod($handler[0], $handler[1]));
202:         } else {
203:             $reflection = (new ReflectionFunction($handler));
204:         }
205: 
206:         $params = $reflection->getParameters();
207: 
208:         // if the handler takes no parameters it is considered global and should
209:         // handle every exception
210:         if (empty($params)) {
211:             return true;
212:         }
213: 
214:         $handlerHint = $params[0]
215:             ->getClass();
216: 
217:         // likewise, if the first handler parameter has no typehint, consider it
218:         // a global handler that handles everything
219:         if (!$handlerHint) {
220:             return true;
221:         }
222: 
223:         return $handlerHint->isInstance($exception);
224:     }
225: 
226:     /**
227:      * Call an exception handler.
228:      *
229:      * @param  mixed     $handler
230:      * @param  Exception $exception
231:      *
232:      * @return mixed
233:      */
234:     protected function callHandler($handler, Exception $exception)
235:     {
236:         if ($handler instanceof ErrorHandlerInterface) {
237:             return $handler->handle($exception);
238:         }
239: 
240:         return call_user_func($handler, $exception);
241:     }
242: 
243:     /**
244:      * Handle an uncaught exception. Does the same as handle(), but also sends
245:      * the response, as we can assume that the exception happened outside of
246:      * HttpKernelInterface::handle.
247:      *
248:      * @param  \Exception $exception
249:      *
250:      * @return Response
251:      *
252:      * @throws Exception  If PHP_SAPI is 'cli'
253:      */
254:     public function handleUncaught(Exception $exception)
255:     {
256:         if (PHP_SAPI === 'cli') {
257:             throw $exception;
258:         }
259: 
260:         return $this->handle($exception)
261:             ->send();
262:     }
263: 
264:     /**
265:      * Handle a PHP error.
266:      *
267:      * @param  int     $level
268:      * @param  string  $message
269:      * @param  string  $file
270:      * @param  int     $line
271:      * @param  array   $context
272:      *
273:      * @throws \ErrorException if the error level matches PHP's error reporting.
274:      */
275:     public function handleError($level, $message, $file = '', $line = 0, $context = array())
276:     {
277:         if (error_reporting() & $level) {
278:             throw new ErrorException($message, 0, $level, $file, $line);
279:         }
280:     }
281: 
282:     /**
283:      * Handle a PHP fatal error.
284:      *
285:      * @return void
286:      */
287:     public function handleShutdown()
288:     {
289:         $exception = $this->makeFatalErrorException();
290: 
291:         if ($exception) {
292:             $this->handleUncaught($exception);
293:         }
294:     }
295: 
296:     /**
297:      * Throw a FatalErrorException if an error has occured.
298:      *
299:      * @return void
300:      *
301:      * @throws \Symfony\Component\Debug\Exception\FatalErrorException
302:      */
303:     public function throwFatalErrorException()
304:     {
305:         $exception = $this->makeFatalErrorException();
306: 
307:         if ($exception) throw $exception;
308:     }
309: 
310:     /**
311:      * Create a FatalErrorException out of the information stored on the last
312:      * PHP error.
313:      *
314:      * @return \Symfony\Component\Debug\Exception\FatalErrorException|null
315:      */
316:     protected function makeFatalErrorException()
317:     {
318:         $error = error_get_last();
319: 
320:         if ($error !== null) {
321:             return new FatalErrorException($error['message'],
322:                 $error['type'], 0, $error['file'], $error['line']);
323:         }
324: 
325:         return null;
326:     }
327: }
328: 
Autarky Framework API documentation generated by ApiGen