Home  >  Q&A  >  body text

Migrating to PHP 8.1 - How to fix deprecated passing null to parameter error - Rename build in function

PHP 8.1 has deprecated passing null as arguments to many core functions. My main problem is with functions like htmlspecialchars(php) and trim(php) where null is no longer silently converted to a null character string.

To solve this problem without using a lot of code, I tried renaming the original built-in functions and replacing them with wrappers that convert the input from null to an (empty) string.

The main problem with this approach is that the function rename_function(PECL apd) no longer works and was last updated in 2004 1.

I need some kind of rewrite of the built-in functions to avoid writing a null check every time the function is called, making all my code twice as big.

The only other solution I can think of is to just use my custom function, but that still requires going through all the code and 3rd party libraries I have.

In PHP 8.1, null is no longer silently converted to an empty string when passed to a built-in function.


  1. https://pecl.php.net/package/apd

P粉420868294P粉420868294306 days ago474

reply all(2)I'll reply

  • P粉811329034

    P粉8113290342024-01-11 15:36:30

    I think (and as a complement, the existing answers have my support) paint a different picture of how such "problems" are viewed and solved. It does not diminish the rightness or wrongness of the approach outlined, but is simply an additional perspective that is hopefully mutually beneficial. Every project is different.

    Given premise:

    Well this looks to me (at first) to be a reporting issue. By not reporting E_DEPRECATED.

    The benefit of this is that (not just your code) now knows that your code comes with a deprecation notice. Reportis indeed valid.

    On the other hand, suppressing deprecation notices may make them disappear. If you lose a code base with a deprecation notice, technically it might still be easy to recover from the loss (again, report the deprecation notice), but if the change is extended, it might now be overwhelming noise (E_TOO_MUCH_NOISE).

    So is code not silent actually a bad thing? Or can it be turned into benefits? I'd rather choose the latter. Regardless, we are already processing this information.

    So, in this case , my idea is to generally not suppress deprecation notifications, but "silence" the function calls. It's easy, but stupid in both a good way and a bad way:

    trim($mixed);   #1  ->     @trim($mixed);   #2

    This is of course an operation that can be applied to the code base using standard text tools. It also shows you where the @ suppression operator has been used in the past:

    @trim($mixed);  #3  ->     @@trim($mixed);  #4

    If you were a PHP developer looking at code like this in an editor (for cases #2-#4) they would immediately scream at you, and for all four cases At least they will attract your attention ($mixed).

    Thank you for not staying silent, we let these places scream, just not at runtime1.

    Unlike the first method of keeping silent by not reporting E_DEPRECATED, this method can easily lose information, which is preserved by using all @ symbols.

    Will it help solve the noise problem? If we stop working here, that's totally not going to work. Now we will paint the code with @- signs and decide to take no further action so that we can use the first solution (which does not report a deprecation message) to complete it without touching the code .

    So what are its benefits? Well, although the code now runs silently, PHP still provides diagnostic messages. That is, it is now possible to register PHP error handlers as listeners (while executing code).

    Only at the code level, it's easy to check these locations because the @ symbols are (usually) easy to spot in the code as well.

    Part Two is important because while multiple places may be affected by deprecation, there must not be one solution that fixes them all (I prefer to stay away from a 'one size fits all' solution scenario'" (if possible), but especially in the context of the question PHP 8.1 has changed and I can imagine there would be different needs depending on where it is used.

    For example, in template code (output), concrete types are not an issue, so converting to string is most likely the preferred solution:

    @trim($mixed);     ->     trim((string)$mixed)
    @@trim($mixed);    ->     @trim((string)$mixed)

    Template (output) remains stable.

    But for actual input handling, a deprecation notice might reveal actual potential flaws worth fixing, such as missing default values ​​(making things overly complicated), unclear handling of values ​​(null vs. null, string, boolean with numbers) with arrays with objects in PHP) or with general $mixed.

    Such trim($mixed) may be a security that has been forgotten for many years and has never been upgraded (there are better security available). For code like this, I'm pretty sure I already want and require that $mixed is actually $string before I use trim (). The reason is very simple, at least two things come directly to mind:

    • a) trim() is no longer needed - it can be removed (one of my favorite fixes: remove code!) - or -
    • b) It is doing string work, then I have a problem because I don't want anything non-string present. The problem is that it generally doesn't work with the shotgun approach (Gießkanne anyone?).

    Is patching using $mixed completely valid? ''If the original usage is a string or null only .

    @trim($mixed);     ->     trim($mixed ?? '')
    @@trim($mixed);    ->     @trim($mixed ?? '')

    But otherwise, numbers like 42, for example, will throw a TypeError instead of a deprecation message. This differentiates between code that is running and code that is not.

    So there is more to maintain here, such as checking locations, further clustering if possible, and then applying more specialized fixes. It might reveal missing tests or assertions, take some time to stabilize the entire application flow, etc.

    In this case, complete the migration of the code, cluster it, handle the null merge operator, and do the appropriate paperwork for the real fix. Once you have completed non-obvious error suppression using the null coalescing operator and removed the @ suppression operator, you may lose this information if the remediation plan does not capture it.

    I'm not surprised when I find myself scratching my head or rubbing my eyes when I look more educated in these parts. Then I remind myself that these errors are not caused by the PHP 8.1 version, the version change just makes them appear (again), and sometimes I even get complete clusters of errors as by-catch by maintaining PHP versions.

    Cheat Sheet

    • (string)$mixed - Previous behavior
    • $mixed ?? '' - Suppress TypeError Error only on null
    • @ - Full error suppression. You should document your codebase where applicable.
    • @@ - If this happens, this might be an interesting place to look into.
    • Empty ($mixed)? '' : xxx($mixed) - Takes the garbage out, the typical empty paralysis/mixed mess, and looks for clusters, with the opportunity to greatly simplify the code base. Migrate to scalar types (PHP 7), introducing strict typing from the inside out, using PHP "classic" and "strict" typing where applicable. PHP 7.0 assertions and PHP 8.1 deprecation messages support this well.

    Error handler

    There is no magic in error handling, it is a documented standard on PHP.net (with Example #1) which acts as an observer for error events and can differentiate between suppressed Errors and unsuppressed errors are handled via error_reporting(php) / error_reporting(php-ini) at least to the level normally required, If needed to differentiate (in a production environment, E_DEPRECATED is usually not part of the report). This example handler throws all reported errors, also for deprecation events and E_ALL, so the @ suppression operator is required not to throw:

    set_error_handler(static function ($type, $message, $file, $line) use (&$deprecations) {
        if (!(error_reporting() & $type)) {
            // This error code is not included in error_reporting, so let it fall
            // through to the standard PHP error handler
    
            // capture E_DEPRECATED
            if ($type === E_DEPRECATED) {
                $deprecations[] =
                    ['deprecations' => count($deprecations ?: [])]
                    + get_defined_vars();
            }
    
            return false;
        }
    
        // throwing error handler, stand-in for own error reporter
        // which may also be `return false;`
        throw new ErrorException($message, $type, error_reporting(), $file, $line);
    });
    

    Similar error handlers can be found in the extended examples at 3v4l.org, including on deprecated code to be reported.

    E_USER_DEPRECATED

    Technically, the error suppression operator can be used in conjunction with E_USER_DEPRECATED in the same way as E_DEPRECATED outlined above.

    However, there is less control over it and it may already be used by third-party code that is already in the project's dependencies. Code similar to the following is not uncommon:

    @trigger_error('this. a message.', E_USER_DEPRECATED);
    

    It does exactly the same thing: emit deprecation events, but exclude them from PHP reporting. Subscribing to these may leave you drowning in noise. With E_DEPRECATED you can always get "good, original" directly from PHP.


    1. IMSoP immediately raises a red/black flag (correctly!) when considering the @ approach to the error suppression operator and commenting on it, it's easy to throw the baby out with the bathwater@ Suppression operator. In my answer, its purpose is just to suppress deprecation notifications but the result of its use is that it suppresses all diagnostic messages and errors, even fatal messages in some PHP versions and errors, so PHP exits with 255 without any further diagnostics - be careful and handle. This operator is powerful. Track its usage in the code base and constantly check if it meets your baseline/expectations. For legal situations, consider using a silencer. For porting/maintaining code, mark it with it first. Once you've finished editing the batch, delete it again.

    reply
    0
  • P粉592085423

    P粉5920854232024-01-11 00:24:47

    First, two things to remember:

    1. PHP 8.1 Deprecated these calls, it will not make them error . The purpose of deprecation is to provide advance notice to authors to fix their code, so you and the authors of the libraries you use can fix issues before PHP 9.0 is released. So don't panic as not all issues can be fixed immediately and be patient with the library maintainers who will fix the issue in their own time.
    2. A quick fix in most cases is to use the null coalescing operator to provide an appropriate default value so you don't need a long null check on every use. For example, htmlspecialchars($something) can be replaced by htmlspecialchars($something ?? '')

    Next, some options:

    • Depending on the number of your cases, you might be able to manually fix a few issues at once, or add ?? '' or fix a logic error, you don't want null anyway.
    • Create custom functions such as nullable_htmlspecialchars and find and replace directly in the code.
    • Create a custom namespace function, such as nullableoverride\htmlspecialchars; Then in any file where you add use function nullableoverride\htmlspecialchars;, that function will be used instead of the built-in function. This must be added to every file, though, so you may need a tool to add it automatically.
    • Using Rector automatically adds ?? '' to the appropriate function calls so you don't have to edit them manually. Unfortunately, there don't seem to be built-in rules for this yet, so you'll have to learn to write your own.
    • Might be simpler, depending on your skills, use regex find and replace to add ?? '' to simple cases.

    reply
    0
  • Cancelreply