Skip to content

Plugin and theme migration guide to PHP 8

onli edited this page Jul 25, 2021 · 5 revisions

For developers targeting PHP 8 with their themes and plugins.

Old Serendipity code clashed with PHP 8 because of a couple common issues, many of those are also to be found in themes and plugins. To avoid and fix those and be compatible with PHP 8, follow these steps:

0. Configure debug and error messages in your dev setup

If serendipity['production'] is true, Serendipity's error handler will suppress all non-fatal errors and warnings. If your goal is to produce good code, set it to false in your serendipity_config.inc.php and fix all warnings. If you just want your code to work, set it to true. Test your plugin. If the code fails with a 500 error page, set it back to false and check the last error message - it will be a fatal error that has to be fixed.

Alpha versions will by default set serendipity['production'] to false. Beta, RC and stable versions set it to true. You can overwrite it manually in serendipity_config.inc.php.

Note that code that goes into the core is not allowed to produce any complaints by PHP. If you want your code to be bundled with serendipity, set serendipity['production'] to false while developing.

1. @ does not work

Silencing errors with @ does not work in PHP 8 anymore. Errors will still be reported and can still block the code execution. Do not use @, fix the error instead. @s that are still in the code can be ignored or removed, though sometimes they can be a nice hint at the developer's intention.

2. No undefined variables

Do not access variables that are not already defined. We did that a lot:

if ($serendipity['GET']['something'] == 'abc') {
   $x = true;
}
if ($x) {  # Error! x can be undefined
   do_something();
}

Initialize x with a default value instead.

Note: This can be ignored for now if serendipity['production'] == true.

3. No undefined keys in arrays

The code in the example above can also fail earlier:

if ($serendipity['GET']['something'] == 'abc') { # This will fail if $serendipity['GET']['something'] was not set
   $x = true;
} 

We need for check for its existence instead:

if (isset($serendipity['GET']['something']) && 
    is_array($serendipity['GET']) && 
    $serendipity['GET']['something'] == 'abc') {
   $x = true;
} 

?? can sometimes be used instead:

if (($serendipity['GET']['something'] ?? '') == 'abc') {
   $x = true;
} 

Note: This can be ignored for now if serendipity['production'] == true.

4. No undefined constants

Accessing a constant that does not exist will produce a fatal error. Only access already defined constants.

5. Use the language API (to avoid a fatal error with constants)

Similarly, redefining constants will produce a fatal error. This happened a lot in themes and plugins with the old default code to load language constants:

$probelang = dirname(__FILE__) . '/' . $serendipity['charset'] . 'lang_' . $serendipity['lang'] . '.inc.php';

if (file_exists($probelang)) {
    include $probelang;
}

include dirname(__FILE__) . '/lang_en.inc.php';  # Error: This will redefine constants when using other languages

Use this instead:

@serendipity_plugin_api::load_language(dirname(__FILE__));

load_language disables the error reporting when reading a language constant file and thus avoids this problem.

6. No invalid PHP constructs

Some old PHP constructs are invalid now. For example each, which was almost exclusively used for while-list-each:

while (list($key, $val) = each($fruit)) {
    echo "$key => $val\n";
}

Use foreach instead.

7. Valid Smarty templates can also not access undefined variables/keys

Very often, smarty templates did something like this:

{if $error_message}
    <div class="error">$error_message</div>   
{/if}

And the PHP code calling this template only set $error_message if there was an error.

As Smarty so far does nothing to prevent it, this will produce a warning with PHP 8. The compiled PHP code tries to access an undefined array key. Future Smarty releases could fix this, but that did not happen yet, so you have to fix it on our side instead.

Either:

  1. Define $error_message in the PHP code also if there is no error, and set it to false.
  2. Check that the variable is defined: {if isset($error_message) AND $error_message}

Note: This can be ignored for now if serendipity['production'] == true.