RSS
 

Posts Tagged ‘ErrorHandler’

Middleware and CakePHP

22 Apr

I started to migrate some of my CakePHP 3.x apps to CakePHP 3.3 middleware and even tested 3.4 already back in 2016.
Until then I didn’t even try the new PSR7 way. But it was about time I caught up here.
I wanted to actually start using some of the Middleware classes available.

I also always wanted to dig deeper into this topic that hasn’t yet too much blog post coverage it seems.
Now, let’s get started here 1.5 years later – but with some very useful gotchas (later in the article more).

Middleware what?

Yeah, this new PSR7 thing – you can also read more about it here.
In fact, it is not that new, but more and more frameworks have been adopting, and so did CakePHP.
When you are upgrading from 3.3 to a newer minor version, you will stumble upon it.

There are also tons of middleware already available out there that – by design – is supposed to work with any PHP framework.
Check out this impressive list.

But now back to CakePHP 3.

First steps

So what to do first to get this new infrastructure working?
The docs explain it quite well:

  • You update your index.php with the new way of dispatching a request (see cakephp/app‘s index.php).
  • You copy and paste the Application.php skeleton into your src/ directory (App namespace). It should be the only class there.

Then you remove your current bootstrap lines around DispatcherFactory::add(...).
You now can add the same functionality as Middleware again in your Application class.

Hands on

Let’s show it with two of my new Middleware classes, "Maintenance" and the improved "ErrorHandler". The first will be an addition, the second one a replacement.

use Setup\Middleware\MaintenanceMiddleware;
use Tools\Error\Middleware\ErrorHandlerMiddleware;
...
class Application extends BaseApplication {
	/**
	 * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
	 * @return \Cake\Http\MiddlewareQueue The updated middleware.
	 */
	public function middleware($middlewareQueue) {
		$middlewareQueue
			// Add Setup plugin Maintenance middleware
			->add(MaintenanceMiddleware::class)
			// Improved Tools plugin ErrorHandler middleware
			->add(ErrorHandlerMiddleware::class)
			// Handle plugin/theme assets like CakePHP normally does.
			->add(AssetMiddleware::class)
			// Finally apply normal routing
			->add(RoutingMiddleware::class);
		return $middlewareQueue;
	}
}

Instead of the ::class way you could also instantiate the classes: new ...Middleware().
But I prefer to keep them strings only until used, especially since I don’t need to pass any arguments for them.

When to use, and when not to use.

The middleware opens a few new possibilities, but it can also introduce more bad code.
You should decide wisely, what can/should be transformed into a middleware class.

Useful cases are usually:

  • Basic low level caching.
  • Serving assets (e.g. a custom WYSIWYG editor serving).
  • Manipulating the request early on, or the response afterwards.
  • Authentication and header parsing, e.g. Token based ones
  • Needing a very early on decision point on certain dispatching options.

You should be careful about:

  • Templating/Rending involved, here usually it is better to use controller (+component?) and normal view rendering.
  • Needing more customization on a controller level, on what parts of the app invoke this logic and which don’t (usually can stay an easy to hook in component).

Now to the gotchas I promised:

Why the improved ErrorHandler?

The core one treats all errors the same by default. Even user-triggered "out of bounds" or not found records are logged into the "error" log.
I find that totally wrong – and even harmful – as it cloaks the actual "internal errors" of higher severity.
So the improved handler provides a way to whitelist certain exceptions as "404" ones that can be triggered from visitors or bots and do not represent internal application errors. It uses the referer to decide whether this is "404" (externally triggered), or an actual issue for error.log (internal reference).
This 404.log then can be occasionally checked and removed, while the error.log as such has to be more closely monitored and everything coming in should be fixed ASAP.

More about the implementation details in the Setup plugin docs.
It is important to also switch out the low level handler in your bootstrap here as well as setting up a specific 404 listener.

Tip: If you use DatabaseLog plugin, you can easily set up a monitoring cronjob to alert the admin (e.g. via email) within seconds of severe issues/errors. With the above "separation" you don’t have false positives and will get only "real" alerts. The plugin built in alert system also makes sure errors are batch-sent to not send out too much emails. Of course you can also limit the scope of alerts further to e.g. only very severe exceptions using the configuration.

Maintenance, what’s that about?

I use this to put the whole application into maintenance mode if there is a more time intensive deployment coming up to assert the users don’t upload images during that timeframe, or put something into the database while it migrates which then breaks.
Usually it is only a few seconds – but it helps to keep the integraty of the website intact.

See my blog post about it, also check the Setup plugin documentation.

The best use case is adding those two commands (enable, disable) into your deploy script, at the beginning and the end. But you can also invoke it through command line manually, when you plan on doing some larger migration or maintenance work.

Other useful middlewares

(Browser) Language detection

The I18n middleware sounds useful if you need to provide multi-locale output and want to detect the best default.
The old approach would have been a component, the more modern way is a middleware here then, for example.

Throttling

The Throttle middleware can be used for certain (API) requests to stay under a certain limit of requests per timeframe.

Your recommendations?

You can comment with your recommended middleware classes, CakePHP or otherwise.

 
No Comments

Posted in CakePHP

 

CakePHP Tips 2017 Part 1

14 Apr

Whoops as ErrorHandler

I recently switched to Whoops in my apps.
It provides a better exception rendering for development mode for most cases.

Just load the plugin (dereuromark/cakephp-whoops) and modify the bootstrap line:

//(new ErrorHandler(Configure::consume('Error')))->register();
(new \CakephpWhoops\Error\WhoopsHandler(Configure::consume('Error')))->register();

If you already have a src/Application.php and run a middleware setup, you also want to make sure you either remove the ErrorHandlerMiddleware there, or provide a custom one for Whoops.

You can test it for example with my sandbox app.
Check it out locally, set it up in minutes and give it a test run.

Jump from browser to IDE

With the Whoops error handler the probably nicest feature is the "click to IDE" one.
It allows to click on the class inside the displayed code piece and jump right into the IDE into the exact line of that file displayed in the browser stack trace.

Be more productive leveraging your IDE

Using the new IdeHelper plugin you can now not only bake annotations into new code, but also adjust
your existing code all the time.
The plugin provides a shell to quickly sync the existing annotations with your code.

This way you can maximize IDE compatibility and with that prevent obvious issues and mistakes.
The IDE will usually mark problematic code yellow (missing, wrong method etc).

It can also enable you to click through the classes and object chains which otherwise would just be plain text and have no typehinting.
Finally autocomplete will be available for all the "magically" provided classes and methods this way.

screenshot

This plugin is supposed to work with ANY IDE that supports annotations.
The plugin wiki contains even details and tips/settings for each of those IDEs – collected by the community.

Enable PHPStan in your app and plugins

PHPStan is a super useful static code analysis tool.
It will help you to find issues with your code way beyond what the test suite or IDE syntax checks can do.

For example, if you have two many different return types and only respect one of them, it will mark this as issue.
It will also tell you about interface mismatch or other similar code smells.

Adjust Travis

Add a new matrix row and a script call for that matrix entry:

...
matrix:
  include:
    ...
    - php: 7.0
      env: PHPSTAN=1 DEFAULT=0
...
script:
  ...
  - if [[ $PHPSTAN == 1 ]]; then composer require --dev phpstan/phpstan && vendor/bin/phpstan analyse -l 1 src; fi
...

Level 1 (-l 1) is the lowest level, once everything passes you can go up to level 5 currently. I recommend trying level 3, if possible.

If you need to ignore certain errors, you can use a custom phpstan.neon file in your /tests directory and then append -c tests/phpstan.neon into the command for travis.

vendor/bin/phpstan analyse -c tests/phpstan.neon -l 1 src

For me such a file looked like this, for example:

parameters:
	autoload_files:
        	- %rootDir%/../../../tests/bootstrap.php
	excludes_analyse:
		- %rootDir%/../../../src/TestSuite/*
	ignoreErrors:
		- '#Call to an undefined method Cake\\Datasource\\EntityInterface\:\:source\(\)#'

Don’t try to ignore too much, though – only "false positives" are recommended to be added here.

You can also write custom PHPStan extensions, see the current CakePHP core /tests/PHPStan folder for details.

Using it locally

I added this to my plugins’s composer.json files:

"scripts": {
    ...,
    "phpstan": "phpstan analyse -l 1 src",
	"phpstan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan && mv composer.backup composer.json"
}

This will allow local testing without messing up your composer file 🙂
For app instead of plugin development you also want to backup-restore the lock file.

Now just run

composer phpstan-setup
composer phpstan

I bet you will find a few useful issues to resolve right on the first run of this tool.

 
3 Comments

Posted in CakePHP