2

I'm trying out zend expressive and this is my config/autoload/zend-expressive.global.php and when I tried to do a request to a path which will go to an action class it returned the error page but I can't see any php error in the apache error log. So I can't tell what's the issue.

Is there a way in zend-expressive to have those php error log? Also any good documentation for zend expressive? It seems the official documentation doesn't really have much examples.

return [
    'debug' => true,    
    'config_cache_enabled' => false,
    'zend-expressive' => [
        'error_handler' => [
            'template_404'   => 'error::404',
            'template_error' => 'error::error',
        ],
    ],
];
edigu
  • 9,878
  • 5
  • 57
  • 80
sparkmix
  • 2,157
  • 3
  • 25
  • 33
  • Zend Expressive v2.0 has been released March 7th and options for error handling are slightly changed and improved in this version. Here is the link for [latest documentation](https://zend-expressive.readthedocs.io/en/latest/features/error-handling/). – edigu Mar 08 '17 at 15:30
  • Hi. I was using this to install. https://docs.zendframework.com/zend-expressive/getting-started/skeleton/ So if I remove everything again and do composer install then will I get the latest version? – sparkmix Mar 08 '17 at 17:52

1 Answers1

4

If you want to try expressive I suggest to use the skeleton installer. It gives you options on what to install. One of the options is the whoops error handler which gives a lot of detailed info about exceptions.

The official docs are here with a lot of info: https://docs.zendframework.com/zend-expressive/

The installer docs: https://docs.zendframework.com/zend-expressive/getting-started/skeleton/

Update: Add ErrorHandler logger example

As a base for your ErrorHandler you can use Zend\Stratigility\Middleware\ErrorHandler. You can attach a listener to that ErrorHandler and use it for logging. Alternatively you can copy that class and modify it to your needs.

Next step is creating a ErrorHandlerFactory for it:

<?php 
// src/Factory/ErrorHandler/ErrorHandlerFactory.php

namespace App\Factory\ErrorHandler;

use Interop\Container\ContainerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use Zend\Diactoros\Response;
use Zend\Expressive\Middleware\ErrorResponseGenerator;
use Zend\Stratigility\Middleware\ErrorHandler;

class ErrorHandlerFactory
{
    public function __invoke(ContainerInterface $container)
    {
        $generator = $container->has(ErrorResponseGenerator::class)
            ? $container->get(ErrorResponseGenerator::class)
            : null;

        $errorHandler = new ErrorHandler(new Response(), $generator);

        if ($container->has(LoggerInterface::class)) {
            $logger = $container->get(LoggerInterface::class);
            $errorHandler->attachListener(function (
                Throwable $throwable,
                RequestInterface $request,
                ResponseInterface $response
            ) use ($logger) {
                $logger->error('"{method} {uri}": {message} in {file}:{line}', [
                    'date'    => date('Y-m-d H:i:s'),
                    'method'  => $request->getMethod(),
                    'uri'     => (string) $request->getUri(),
                    'message' => $throwable->getMessage(),
                    'file'    => $throwable->getFile(),
                    'line'    => $throwable->getLine(),
                ]);
            });
        }

        return $errorHandler;
    }
}

After that you need to register the ErrorHandler. You do this by adding it to config/autoload/middleware-pipeline.global.php and specifically in the middleware => always section. This way it will always run. If you register it as first, it will run before anything else.

<?php 
// in config/autoload/middleware-pipeline.global.php

use Acme\Container\ErrorHandlerFactory;
use Zend\Stratigility\Middleware\ErrorHandler;

return [
    'dependencies' => [
        /* ... */
        'factories' => [
            ErrorHandler::class => ErrorHandlerFactory::class,
            /* ... */
        ],
        /* ... */
    ],
    'middleware_pipeline' => [
        'always' => [
            'middleware' => [
                ErrorHandler::class,
                /* ... */
            ],
            'priority' => 10000,
        ],
        /* ... */
    ],
];
xtreamwayz
  • 1,285
  • 8
  • 10
  • Is there a way to just display those php errors instead of/beside displaying the error page? It seems those whoops error handler requires you to add code or throw exception in your code. I just want to see those php errors like when it has the 500 internal error and the details. – sparkmix Mar 03 '17 at 15:13
  • Okay I got it now. Thanks. I found this as I didn't enable whoops. http://www.masterzendframework.com/whoops-errorhandler/ – sparkmix Mar 03 '17 at 15:23
  • That error handler is only for local/dev and I wonder if there's a way to enable the php error reporting in the apache error log in production? This only helps for development but I can't see any detail errors in production. – sparkmix Mar 03 '17 at 15:29
  • @sparkmix You can write your own errorhandler to not only catch the errors but also log them. Here's an example of something I use myself: [ErrorHandlerFactory](https://github.com/xtreamwayz/xtreamwayz.com/blob/4a53739ab68ede03db769cfe899406016fefdaaf/src/Factory/Infrastructure/ErrorHandler/ErrorHandlerFactory.php) – xtreamwayz Mar 03 '17 at 17:18
  • The errorhandler and its listener is documented in the stratigility docs: https://docs.zendframework.com/zend-stratigility/error-handlers/#errorhandler-listeners – xtreamwayz Mar 03 '17 at 17:32
  • I'm still trying to understand those factory. So you put your ErrorHandlerFactory in your routes.global.php ? – sparkmix Mar 03 '17 at 22:54
  • No, routes are only executed if a specific route is requested. Basically what you do is setup a factory dependency of the ErrorHandler you are using and point it to ErrorHandlerFactory. I can't exactly say where to place that ErrorHandlerFactory since I don't know how you have setup expressive and what version you are using. You have any code on github? – xtreamwayz Mar 05 '17 at 06:13
  • I just used the same skeleton install you provided and there's only FastRouteRouter in the routes.global.php at the beginning but there're some factories & routes we added but those are really php test code we're trying. So I was wondering if I include your ErrorHandlerFactory in the dependencies => factories then will any middleware inside the routes config will use this factory? Base on the example or test we do the factory class with __invoke to call the action class we specified in the routes. I'm just wondering how to use the error handler you mentioned? – sparkmix Mar 06 '17 at 15:25
  • Hi Thank you for the info. Is this mean it will use this errorhandler and not use the 'Zend\Expressive\FinalHandler' => Zend\Expressive\Container\TemplatedErrorHandlerFactory::class ? It seems now it shows "internal server error" while without the templates/error Just wonder if it's possible to have that TemplatedErrorHanderFactory running while have yours doing the logging. – sparkmix Mar 07 '17 at 20:09
  • Please read this: https://docs.zendframework.com/zend-expressive/reference/migration/to-v1-1/#error-handling and especially about the `TemplatedErrorResponseGenerator`. If you combine that with what I wrote above you have everything you need. – xtreamwayz Mar 07 '17 at 20:12
  • @xtreamwayz Thanks! I just had this question, When I attach error listeners to the `ErrorHandler` in my `ErrorHandlerFactory`. Is it okay to attach a listener which uses PHP's `error_log()` to log the errors to php error log file? Since the error is not thrown again from the `ErrorHandler` it will never reach the PHP's error handler. – Ashish Ranjan Feb 08 '18 at 11:34