Skip to content

Kirby 3.7.5

Basic error hunting & prevention

When we develop our Kirby-powered website and all of a sudden all we get is a blank page or our page stops rendering mid-way, or we get a result that is different from what we expect, it's time to do some debugging. Often, it's just a tiny syntax error, sometimes it's more complicated than that. This recipe collects some basic debugging and error prevention tips.

Turn on debugging

By default, Kirby respects the settings of your php.ini file. The ideal way to enable/disable debugging is therefore in your php.ini file. If you don't want to change these settings for whatever reason, Kirby's debug option offers a quick way to display PHP errors. To enable this option for development, put this line into in /site/config/config.php:

/site/config/config.php
return [
    'debug' => true
];

This option configures PHP so that errors are displayed on the screen. PHP errors contain a message about the type of error together with the file and line number where that error occurred. Note that the error won't always be in the code on the given line, but may occur before or after that line.

Disable debugging on live server

Make sure to disable debug mode in production! Displaying PHP errors on a public server can be a serious security risk:

  • Error messages are displayed with detailed information about the code structure (e.g. file path, class, method)
  • With Whoops enabled, there will be even more detailed information about the code structure
  • Detailed error messages for login failures could leak information to attackers

In a production environment, always log errors to your PHP error logs.

Usually, production servers should be configured to not display errors on the screen but log them to a log file. However, if we forget to disable our config settings in our production environment, these server settings will be overwritten.

To avoid that, we can use domain specific config.php files. Always set

/site/config/config.php
return [
    'debug' => false
];

in the config.php file for your production server. Make sure that you have access to a PHP error log file, though. If you cannot find an error log file, it might be possible to configure PHP to create a log file in your php.ini file or in your .htaccess:

# php.ini
error_reporting = E_ALL
log_errors = On
display_errors = Off
error_log = /your/path/public_html/logs/php-error.log
# .htaccess
php_flag  log_errors on
php_flag display_errors off
php_value error_log  /your/path/public_html/logs/php_error.log

Make sure to protect the logs folder from being publicly accessible.

You can probably find more information in the help section of your hosting provider, or contact them for help with setting up your environment.

Finding your PHP error logs

When you don't know the path to the PHP error logs (e.g. on shared hosting), you can use the phpinfo() command to find this path.

Call this command from a separate PHP file or from one of your templates, then open the file in your browser and search for error_log.

<?php

phpinfo();

Alternatively, you can use

$errorLog = ini_get('error_log');
var_dump($errorLog);

to get the path.

Sometime, error logs are not enabled. If there is no error log, you might have to set it first via php.ini as described above.

Typical PHP errors

PHP has several levels of error messages based on severity. The most common ones - as defined on the official PHP website - are:

  • Errors: "Fatal run-time errors. These indicate errors that can not be recovered from, such as a memory allocation problem. Execution of the script is halted."
  • Parse Errors: "Compile-time parse errors. Parse errors should only be generated by the parser."
  • Notices: "Run-time notices. Indicate that the script encountered something that could indicate an error, but could also happen in the normal course of running a script."
  • Warnings: "Run-time warnings (non-fatal errors). Execution of the script is not halted."

Here are some typical errors with their error levels:

  • missing semicolon after a PHP statement (Parse error: syntax error)
  • missing or extra parenthesis (Parse error: syntax error)
  • unclosed braces (Parse error: syntax error)
  • unclosed quotes (Parse error: syntax error)
  • undefined variables, sometimes just because of typos (Notice: Undefined variable ...)
  • use of = instead of == when comparing values in an if-statement
  • call to undefined functions, sometimes just because of typos (Fatal error: Call to undefined function)
  • not checking if an object exists before calling a method on it (Fatal error: Uncaught Error: Call to a member function uri() ...)
  • using incorrect parameters in a function call
  • script using too much memory, in the Kirby context, when var_dump()ing a large object (Fatal error: Allowed memory size of 67108864 bytes exhausted)

PHP error logging will not, however, reveal errors in the functionality of our code, because these don't throw PHP errors.
When we deal with logic errors, these are often hard to find. We may have been expecting an if statement to be true, when indeed it is false.

Echoing values

Putting echo() statements into our code at strategic points can help pinpointing such bugs by checking if the script generates the expected values.

Checking the value of variables

var_dump() and print_r are both very useful PHP functions that accept multiple types of input values including arrays and objects. Arrays and objects are explored recursively with values indented to show their structure.

There are some differences worth knowing, though.

print_r()

  • outputs a human_readable representation of one value
  • does not output the type of values
  • may return its output as a return value if the second optional argument is given

var_dump()

  • outputs a representation of one or more values
  • outputs the type of values
  • does not have a return value

Let's look at some example output from both functions using the array we get from our events example to see those differences at work:

$events = $page->events()->yaml();
#print_r($events)
Array
(
    [0] => Array
        (
            [event_title] => Cabramatta Moon Festival
            [event_date] => 2016-09-04
            [event_location] => Sydney
            [event_image] => cabramatta.jpg
        )

    [1] => Array
        (
            [event_title] => Paris Hip-Hop Festival
            [event_date] => 2016-07-04
            [event_location] => Paris
            [event_image] => hip-hop.jpg
        )

    [2] => Array
        (
            [event_title] => Pyronale
            [event_date] => 2016-09-09
            [event_location] => Berlin
            [event_image] => pyronale.jpg
        )

)
#var_dump($events)
array (size=3)
  0 =>
    array (size=4)
      'event_title' => string 'Cabramatta Moon Festival' (length=24)
      'event_date' => string '2016-09-04' (length=10)
      'event_location' => string 'Sydney' (length=6)
      'event_image' => string 'cabramatta.jpg' (length=14)
  1 =>
    array (size=4)
      'event_title' => string 'Paris Hip-Hop Festival' (length=22)
      'event_date' => string '2016-07-04' (length=10)
      'event_location' => string 'Paris' (length=5)
      'event_image' => string 'hip-hop.jpg' (length=11)
  2 =>
    array (size=4)
      'event_title' => string 'Pyronale' (length=8)
      'event_date' => string '2016-09-09' (length=10)
      'event_location' => string 'Berlin' (length=6)
      'event_image' => string 'pyronale.jpg' (length=12)

As you can see, while var_dump() provides more information, it can easily get a bit complex when outputting recursive arrays or objects.

Using breakpoints

The die() function stops the execution of a PHP script. Any code that follows after a die() statement is not executed. Thus, such a statement helps you to find out if parts of your code are executed and if your code fails before or after the statement.

Let's look at a simple example:

// we create an array of tags from all child pages of the current child
$tags = $page->children()->pluck('tags', ',', true);

// if the $tags array is not empty, the execution of the script is halted with a message
if (!empty($tags)) {
    die('Breakpoint 1: $tags is not empty');
}

Avoiding errors

  • use an editor with syntax highlighting
  • always indent your code properly to enhance readability, a good editor can also help with this
  • use comments to keep track of what your code is supposed to be doing
  • always use if statements to check if an object exists before calling a method on it

Tools

You might also be interested in checking out some PHP debugging tools.

Further reading

First steps to start debugging with Xdebug