Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

Nullable parameters and type declarations

Fix the most common PHP 8.4 deprecation, implicitly nullable parameters, and add type declarations to old code safely, with examples from expsite_api, expquery_translator and ezfind.

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/help/extension-development/part-4-upgrading/nullable-parameters-and-types.md

On this page

Nullable parameters and type declarations

Part 4, chapter 8. This chapter covers the deprecation you will see most often in old code on PHP 8.4 and later, the implicitly nullable parameter, and its close relative, an optional parameter before a required one. It also gives rules for adding type declarations to old code without breaking callers.

The deprecation

Deprecated: myOldExtHelper::render(): Implicitly marking parameter $node as nullable is deprecated,
the explicit nullable type must be used instead

It comes from a parameter that has a class or a type, and null as its default:

function render( eZContentObjectTreeNode $node = null )

PHP has always allowed null here because of the default. PHP 8.4 wants you to say so in the type.

The fix

Add a question mark:

function render( ?eZContentObjectTreeNode $node = null )

That is all. ?Type works since PHP 7.1, so the fix runs on every PHP Exponential 6 supports, and callers are not affected: they could pass null before and they still can.

From se7enxweb/expsite_api, commit 0f708f4:

// before
public function __construct( eZContentObject $object = null )

// after
public function __construct( ?eZContentObject $object = null )

The commit message names the reason it mattered: the old form was reported "on every request that builds one (the search page does)". se7enxweb/expquery_translator, commit 8ddf68d, made the same change to 35 parameters in 27 files, and verified it with php -l on PHP 8.0 and 8.5 and by running a query through the parser on both.

Find every case

grep -rnE '[(,]\s*(array|callable|string|int|float|bool|iterable|object|self|[A-Z][A-Za-z0-9_\\]+)\s+&?\$[A-Za-z0-9_]+\s*=\s*null' --include='*.php' extension/myoldext/

Every hit without a ? before the type, and without |null in a union type, is one to fix. PHP 8.4's own syntax check reports them too:

find extension/myoldext -name '*.php' -print0 | xargs -0 -n1 php8.4 -d error_reporting=-1 -l 2>&1 | grep -i nullable

(Use the path of a PHP 8.4 or 8.5 binary on your system instead of php8.4.)

Interfaces and parent classes

If the parameter belongs to a method that implements an interface or overrides a parent's method, fix the interface or the parent first, then every implementation. In se7enxweb/expquery_translator the abstract Visitor::visit( Node $node, ?Visitor $subVisitor = null, $options = null ) and all its implementations changed in the same commit. PHP accepts a child that is more permissive than the parent, so changing them in the same commit keeps every step loadable.

If the parent is a kernel class, look at the kernel's current signature and copy it. The kernel's own methods were changed for PHP 8.4 and 8.5 between 6.0.8 and 6.0.11 (PHP 8 support), so an override written against an older kernel can differ from its parent today.

Optional parameters before required ones

The second deprecation in this family is a default value on a parameter that is followed by a required one:

// a default that can never be used: $handlerParameters is required after it
protected function buildBoostFunctions( $boostFunctions = null, &$handlerParameters )

PHP 8.0 reports this as deprecated, because the default can never apply. From se7enxweb/ezfind, commit 4134cbb:

// after: the default removed, the only caller passes both
protected function buildBoostFunctions( $boostFunctions, &$handlerParameters )

Before removing the default, check every caller. If some caller leaves out the first argument, it was already broken.

Adding type declarations to old code

Type declarations make errors appear where they are made instead of three calls later. Adding them to an old extension is worth it, but only where you control every caller.

WhereAdd types?
Private and protected methods of your own classesyes
Public methods other extensions may callcarefully: a stricter type breaks callers that passed something else
Methods that override kernel methodsno: copy the parent's signature exactly
Template operator modify(), datatype methods, workflow execute()no: the kernel calls them with the signature of the base class
Return types of methods implementing PHP interfaces (Iterator, ArrayAccess, Countable)yes, or #[\ReturnTypeWillChange] (PHP 8.1 to 8.5 deprecations)

Types you can use on PHP 8.0, the oldest PHP of Exponential 6: scalar types, ?Type, union types (int|string), mixed, static as a return type. Types you cannot use on 8.0: never, intersection types, null or false as a type of their own, readonly properties, enums.

Kernel values are often false where newer code would use null. eZContentObject::fetch() returns null when nothing is found, but many older kernel functions return false. A return type ?eZContentObject on your own wrapper is wrong if it passes such a false through. Check what the kernel method returns before you declare it:

<?php
class myOldExtObjectLoader
{
    /**
     * @return eZContentObject|null
     */
    public static function load( int $objectID ): ?eZContentObject
    {
        $object = eZContentObject::fetch( $objectID );
        return $object instanceof eZContentObject ? $object : null;
    }
}

The instanceof check turns whatever the kernel returned into the type you promised.

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x