Migration to PHP 8.1 - how to fix Deprecated Passing null to parameter error - rename build in functions
Asked Answered
A

14

88

PHP 8.1 has deprecated passing null as parameters to a lot of core functions. My main problem is with functions like htmlspecialchars(php) and trim(php), where null no longer is silently converted to the empty string.

To fix this issue without going thrugh huge amount of code I was trying to rename original built-in functions and replace them with wrappers that cast input from null to (empty) string.

My main problem with this approach is, that the function rename_function(PECL apd) no longer works, last update on this is from 20041.

I need some sort of override of built-in functions, to avoid writing null check each time function is called making all my code two times larger.

Only other solution I can think of is to use only my custom functions, but this still require going thru all my code un and third party libraries I have.

In PHP 8.1 when null is passed to build in function, it is no longer silently converted to empty string.


  1. https://pecl.php.net/package/apd
Anthea answered 1/4, 2022 at 13:0 Comment(1)
for me it's a signal to change the language. does .py do the same? all my code looks like ??''. wth? what's the benefits?! php make jit! was fast with opcache and memcache as Java, but without mem leakage.. so said..Alexandra
J
96

Firstly, two things to bear in mind:

  1. PHP 8.1 deprecates these calls, it does not make them errors. The purpose of deprecation is to give authors advance notice to fix their code, so you and the authors of libraries you use have until PHP 9.0 comes out to fix things. So, don't panic that not everything is fixed right away, and be patient with library maintainers, who will get to this in their own time.
  2. The quick fix in most cases is to use the null coalescing operator to provide a default value as appropriate, so you don't need a long null check around every use. For instance, htmlspecialchars($something) can be replaced with htmlspecialchars($something ?? '')

Next, some options:

  • Depending how many cases you have, you may be able to just fix them manually a few at a time, either adding ?? '' or fixing a logic bug where you weren't expecting a null anyway.
  • Create custom functions like nullable_htmlspecialchars and do a straight-forward find and replace in your code.
  • Create custom namespaced functions like nullableoverride\htmlspecialchars; then in any file where you add use function nullableoverride\htmlspecialchars; that function will be used instead of the built-in one. This has to be added in each file, though, so you may need a tool to automate adding it.
  • Use Rector to automate adding ?? '' to appropriate function calls, so you don't have to edit them all by hand. Unfortunately, there doesn't seem to be a built-in rule for this (yet), so you'd have to learn to write your own.
  • Possibly simpler, depending on your skills, use a regular expression find-and-replace to add the ?? '' to simple cases.
Jodhpurs answered 2/4, 2022 at 16:29 Comment(13)
I will try to use strval function to convert null to empty string to make it run, if needed, on php before version 7.0, to support wider range of installations.Anthea
Interestingly this deprecation notice is a PHP Fatal error for me. Fresh install of PHP 8.1.2 through Apache, with no fancy configurationUralian
@Uralian Either a) you actually have a different message, for something that was changed in 8.0; or b) you do in fact have some "fancy configuration" - most likely, a custom error handling function which (illogically) promotes all deprecations to fatal errors.Jodhpurs
While just a deprecation warning in the error logs, when error logging was enabled (dev environment), it was failing to return any data for me in an AJAX call.Orthodontia
@BeninCA That would be because you are displaying errors; you need to change your configuration to log messages, or capture and format them; either for the whole application, or for those pages where you need to output well-formed data. There is nothing new about this particular deprecation message that behaves differently from any other Notice, Warning, etc.Jodhpurs
@IMSop Right - I know, I just figured I'd include that comment as a reminder for others - because the "displayed" errors don't always "display".Orthodontia
If null is the only cause of this I wonder why not a simple string cast is considered appropriate or if it only about an access condition when sinking those values prefixing at those sinks with the @ error suppression operator for a start. the cast would express the intention, the @ operator allows later control (e.g. handling the deprecations, marking the places - string cast, null-coalescing etc. could hide the underlying defect or at least make it hard to review later).Incongruent
@Incongruent If anything @ is much "blunter" than (string) or ?? '' because it suppresses all diagnostics on an expression - echo htmlspecialchars( @foo() ); suppresses everything in the implementation of foo() and everything it calls in turn, whereas echo htmlspecialchars( foo() ?? '' ); runs foo() as normal, then handles the specific case of a null return. @ also apparently has a noticeable performance hit. If you just want to mark where you've seen a deprecation notice, just use a TODO comment - the whole point of deprecations is that you don't need to handle them immediately.Jodhpurs
@IMSoP: Yes, that's how @ works, but thinking more that if the value already was the problem, it would not have been the use-case. e.g. in the foo() scenario, that is likely otherwise under control and would benefit from the return type hint regardless. the @ would only for dedicated use as in scope of the question, but you're right it should be commented on those consequences. BTW. when reading a comment under the rector answer I came to the conclusion that the (string) cast is most likely best preserving the previous behaviour.Incongruent
@Incongruent Sorry, I don't understand what you think the advantage of @ is. You still need to fix the problem before 9.0 anyway, so all you're doing is removing a message from your logs. Better to just filter the log files, or turn off all E_DEPRECATED messages except during specific test runs.Jodhpurs
well you can track on the depcrecation notices with error handling, so you can treat all of them at a central location, after learning where they are applicable. a fix could depend on context, e.g. I could imagine within template code it's the string case where elsewhere the notice may highlight an underlying issue that needs more dedicated fixing. QC and at least some of the fixes can be easily automated then I can imagine. With deprecation warnings I found the @ operator already pretty useful long before PHP 8.1 release.Incongruent
@Incongruent Feel free to add your own answer explaining how you use that then; I won't be adding it to this one, because I don't understand what you mean, and generally avoid any use of @Jodhpurs
@IMSoP: Text-walled it: https://mcmap.net/q/237993/-migration-to-php-8-1-how-to-fix-deprecated-passing-null-to-parameter-error-rename-build-in-functions - it's more an additional concept and the at suppression likely only an intermediate.Incongruent
T
10

Rector has the rule NullToStrictStringFuncCallArgRector to fix this:

-     mb_strtolower($value);
+     mb_strtolower((string) $value);
Tbar answered 8/12, 2022 at 12:57 Comment(4)
I like that better than $value ?? ''Fawnia
@RyanMortier: Depends. One should be aware with the string cast that it turns arrays into the string "Array" while giving a warning (notice before PHP 8) ref. So you have to consider the appropriate handling beforehand (or at least should when applying automated changes to the overall code-base), to not run into the next maintenance problem. Rector likely does it this way because this is what would happened before, so it preserves the original behaviour.Incongruent
@Incongruent A big difference is that if you are running with strict_mode=1, then htmlspecialchars(42) is already an error; htmlspecialchars(42 ?? '') retains that error, htmlspecialchars((string)42) suppresses it. Similarly, (string)$object will call $object->__toString(), $object ?? '' will not. On the other hand, htmlspecialchars($mistypedVar ?? '') suppresses a Warning which htmlspecialchars((string)$mistypedVar) does not, so neither is a perfect replacement.Jodhpurs
My strong educated guess in context of the question is that htmlspecialchars() and trim() is a signal that not strict types but "string" types is what OP is aiming for, most likely code related to output (template etc.). This is not what strict_mode is of a particular benefit of, so would only add another error as false positive (even a real one, not a deprecation warning). The reason why the internal functions were typed was a different one than the strict_mode IIRC, and I have some sympathy for the cause here, but fear it's not only null but also the 42.Incongruent
I
8

I'd like (as an addition, existing answers have my upvotes) to paint a different picture on how to see and tackle with such "problems". It does not make the outlined approaches less right or wrong and is merely an additional view which hopefully is of mutual benefit. And every project is different.

Given the premise:

My main problem is with functions like htmlspecialchars(php) and trim(php), where null no longer is silently converted to the empty string.

then this looks (first of all) as a reporting problem to me. The code can be made silent by not reporting E_DEPRECATED.

Doing so ships (not only your code) with the benefit, that it is now known that your code is with deprecation notices. Reporting did work.

On the other hand, silencing deprecation notices may move them out of sight. And if you loose the information that the code-base is with deprecation notices, it may still be technically easy to recover from that loss of information (report deprecation notices again), however if the time from change has enlarged, there might now be an overwhelming noise (E_TOO_MUCH_NOISE).

So is the code not being silent actually a bad thing? Or can it be turned into a benefit? I'd prefer to go with the later. We're working with the information already anyway.

So in this case I had the idea to not generally suppress the deprecation notices but to "silence" the function calls. It is easy, and there is stupidity both in the good but also in the worse sense with it:

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

This is certainly an operation that can be applied on a code-base with standard text tooling. It would also show you where use of the @ suppression operator already was made in the past:

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

If you're a PHP developer looking at such code within your editor (for cases #2-#4), they would immediately scream to you and for all four cases raise your eyebrows at least ($mixed).

So thanks for not being silent, we made the places screaming, just not at runtime1.

Different to the first approach to silence by not reporting E_DEPRECATED which is prone to loosing information, the information is preserved by having all the @-signs.

Does it help with the noise problem? If we stop doing the work here, not at all. Now we would have plastered the code with @-signs, decided to take no further action so we could have already done it with the first solution (do not report deprecation message) without touching the code.

So what is the benefit of it? Well, despite the code now running silent, PHP still is providing the diagnostic messages. That is, it is now possible to register a PHP error-handler as a listener (when executing the code).

And just on code-level, it is easy to review the places as the @-signs are (typically) easy to spot in the code as well.

The second part is important, as albeit multiple places may be affected by a deprecation, there must not be one fix to catch them all (me prefers to stay away from "one size fits it all 'solutions'" if possible), but especially with this PHP 8.1 change in context of the question, I can imagine there are different needs depending on place of use.

For example in templating code (output) the concrete type is less an issue, and therefore a cast to string is very likely the preferred fix:

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

The templating (output) remains stable.

But for actual input processing, the deprecation notice may uncover actual underlying flaws that are worth to fix, like missing defaults (over complicating things), unclear handling of values (empty vs. null vs. string vs. bool vs. number vs. array vs. object in PHP) or a general $mixed confusion.

Such a trim($mixed) could be a years old forgotten safe-guard that has never undergone the upgrade (there are better safeguards available). For such code I'm pretty sure I already want and demand that $mixed actually is $string before I make use of trim(). The reason is simple, at least two things are coming to mind directly:

  • a) Either trim() is not necessary any more - than it can be removed (one of my favorite fixes: removing code!) - or -
  • b) It is doing string-work then I have a problem as I don't want anything not a string to be there. Problem in the sense, that it is often not applicable with a shotgun approach (Gießkanne anyone?).

It is totally valid to patch with $mixed ?? '' if the original use was string or null only.

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

But otherwise, e.g. a number like 42, instead of a deprecation message, a TypeError is being thrown. This can make the difference between running and not running code.

So there is a bit more to maintain here, like reviewing the places, further clustering if possible and then applying more dedicated fixes. It could reveal missing tests or assertions, need a bit of a time to stabilize in the overall application flow etc..

Under such cases to complete the migration of the code, do the clustering, handle w/ null-coalescing operator and do the proper paper-work for the real fixes. Once the non-obvious error suppression with the null-coalescing operator has been done and the @ suppression operator removed, you'll likely loose the information if the fix planning has not captured them.

When looking more educated at such places, I'm not surprised when I find myself scratching head or rubbing my eyes. Then I remind myself that those errors are not because of the PHP 8.1 version, the version change has just brought them up (again) and I get sometimes even complete bug clusters as a by-catch only by maintaining the PHP version.

Cheat-Sheet

  • (string)$mixed - previous behaviour
  • $mixed ?? '' - error suppression for TypeError on null only
  • @ - full error suppression. you should document in/for your code-base where it is applicable to use.
  • @@ - if this comes up, it is likely an interesting spot to look into.
  • empty($mixed) ? '' : xxx($mixed) - carry the trash out, typical empty paralysis / mixed confusion, look for clusters, there is a chance the code-base can be greatly simplified. migrate to scalar types (PHP 7), pull in strict type handling from most-inner in outwards direction, use both PHP "classic" and "strict" type handling where applicable. PHP 7.0 assertions and PHP 8.1 deprecation messages can support here well.

Error Handler

There is no magic with the error handling, it is standard as documented on PHP.net (compare with Example #1), it works as an observer on the error events, distinction between suppressed and non-suppressed errors can be done via error_reporting(php) / error_reporting(php-ini) at least to the level normally necessary if the distinction is needed (in a production setting E_DEPRECATED is normally not part of the reporting). This exemplary handler throws on all reported errors, so would for deprecation events as well for E_ALL and would therefore require the @ suppression operator to not 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);
});

An error handler similar to it can be found in an extended example on 3v4l.org including deprecated code to report on.

E_USER_DEPRECATED

Technically, the error suppression operator can be combined with E_USER_DEPRECATED the same as outlined with E_DEPRECATED above.

However there is less control about it and it may already be in use by third-party code a project may already have in its dependencies. It is not uncommon to find code similar to:

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

which does exactly the same: emit deprecation events but exclude them from PHPs' reporting. Subscribing on those may put you into the noise. With E_DEPRECATED you always get the "good, original ones" from PHP directly.


  1. When considered the approach with the @ error suppression operator and commented on it, IMSoP immediately raised a red/black flag (rightfully!) that it is easy to throw the baby out with the bathwater with the @ suppression operator. In context of my answer it is intended to only suppress the deprecation notice but the consequence of use is that it suppresses all diagnostic messages and errors, in some PHP versions even fatal ones, so PHP exits 255 w/o any further diagnostics - not only take but handle with care. This operator is powerful. Track its usage in your code-base and review it constantly that it matches your baseline/expectations. For legit cases consider to make use of a silencer. For porting / maintaining the code use it for flagging first of all. When done with mass-editing, remove it again.
Incongruent answered 13/12, 2022 at 21:42 Comment(2)
then this looks (first of all) as a reporting problem to me. The code can be made silent by not reporting E_DEPRECATED - bad solution. E_ALL always on dev - everything must be just clean and smoothAlexandra
@VasiliiSuricov: Solution? That's a description of a problem! You suggest E_ALL yourself to tackle with it (we may suggest the default of -1 when registering an error handler which is pretty much equivalent these days but has better properties if you run a build matrix over many PHP versions.) Not to forget to make assertions throwing, it's not that every php-development.ini has this activated since 7.0 by default (where assertions actually became powerful ... now if we register a -1 throwing error handler in an assert expression ...) Wasn't it even only relatively recently?Incongruent
S
7

A solution for existing projects with a lot of pages, that you want to migrate to PHP8+:

In my case, most problems came with "trim" function that receives null values. In that case, you can create a custom "trim" function and then replace in your existing code the "trim" for "custom_trim" function:

public function custom_trim(?string $value)
{
    return trim($value ?? '') ;
} 

or just cast the parameter like this

   trim((string) $value);
Sophisticate answered 15/6, 2022 at 9:31 Comment(6)
Why trim an empty string? Is this better: return empty($value) ? '' : trim($value)?Anthropoid
When you're working on legacy code, with thousands of pages that someone decided to migrate to php8 and you've to fix the platform to be live again, this is a quick and easy solution.Sophisticate
@kiatng: the number zero (or the string representation of it) is not correctly trimmed to the empty string but to the string "0". an empty(string) in PHP can be different from a zero-length string.Incongruent
@chispitaos: Platform to be live again? What caused your production platform to go down only by deprecation messages? Could you add a bit of context? (sorry if this appears me to be curious, but I'd like to understand a bit better) (and yes, the solution looks legit and correct to me, that is no criticism of your answer of any kind)Incongruent
hi @hakre.. it happens when your customer press upgrade without consulting you first, and their platform was live.. ;)Sophisticate
Yeah, that's clever. "Oh, this shiny red upgrade button looks so nice. I wonder what may happen when I press it." Let me guess, there was no snapshot?Incongruent
H
3

The OP's issue is that refactoring a large code base is hard. Adding ??'' to every strlen() call is a major time sink when your dealing with many MB of legacy source code.

Type conversion works on null values such that

strlen((string)null); // returns 0

A search and replace of strlen( with strlen((string) can work but you'd still need to step through them one at a time to look for edge cases.

Hardnett answered 14/11, 2022 at 4:12 Comment(2)
Using a string/NULL value by casting to a string using a (string) prefix cast is surely better than using ?? '', as the latter could wrongly interpret string '0' as evaluating to false.Ultimogeniture
?? '' would not interpret '0' as null. ?? only catches NULL values. It's just harder to pull off in a refactor. You can't do a search and replace with that even with AI helping.Hardnett
O
2

What a painful experience this is.

Here's my quick solution which is compatible with older versions of php:

function custom_trim(  $value)
{

    if($value!="")
    {
        $value = trim($value);
    }
    return $value;
} 


function custom_stripslashes(  $value)
{

    if($value!="")
    {
        $value = stripslashes($value);
    }
    return $value;
} 
 
Octo answered 26/6, 2023 at 4:30 Comment(0)
P
1

while waiting to correct the problems (there may be many) it is possible to define a custom error handling function to ignore them.

For exemple :

error_reporting(E_ALL) ;
set_error_handler(
    function($severity, $message, $file, $line) {
        if ( !$severity || error_reporting()!=E_ALL ) return ;  // to treat @ before functions
        $erreurs_autorisees = array(
            E_NOTICE          => "Notice",
            E_USER_NOTICE     => "User Notice",
            E_DEPRECATED      => "Deprecated",
            E_USER_DEPRECATED => "User Deprecated",
            E_WARNING         => "Warning",
            E_USER_WARNING    => "User Warning",
        ) ;
        if ( isset($erreurs_autorisees[$severity]) ) {
            $warning_autorises = [
                "addslashes(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "base64_decode(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "htmlspecialchars(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "mb_decode_mimeheader(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "mysqli_real_escape_string(): Passing null to parameter #2 (\$string) of type string is deprecated",
                "preg_replace(): Passing null to parameter #3 (\$subject) of type array|string is deprecated",
                "preg_split(): Passing null to parameter #3 (\$limit) of type int is deprecated",
                "rawurlencode(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "setcookie(): Passing null to parameter #2 (\$value) of type string is deprecated",
                "str_starts_with(): Passing null to parameter #1 (\$haystack) of type string is deprecated",
                "strcmp(): Passing null to parameter #1 (\$string1) of type string is deprecated",
                "strlen(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "strtr(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "strpos(): Passing null to parameter #1 (\$haystack) of type string is deprecated",
                "substr(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "trim(): Passing null to parameter #1 (\$string) of type string is deprecated",
                "strncasecmp(): Passing null to parameter #1 (\$string1) of type string is deprecated",
            ] ;
            if ( in_array($message, $warning_autorises) ) return true ;
            
            // On ne converti pas les warning en Exception, on se contente de les logger / les afficher
            $msg = $erreurs_autorisees[$severity].": $message in $file on line $line" ;
            if ( ini_get('display_errors') ) echo $msg ;
            // @error_log($msg) ;  // if you want to log
        }
        else throw new ErrorException($message, 0, $severity, $file, $line) ;
        return true;
    }
);
Pneumonic answered 20/8, 2022 at 19:25 Comment(0)
B
1

WPCS is incompatible with PHP 8.1. Adding this to your phpcs config file may fix it for you.

<ini name="error_reporting" value="E_ALL &#38; ~E_DEPRECATED" />

(Note that the &#38; is an escaped & (ampersand); the & character must be escaped in XML documents.)

Reference - https://github.com/WordPress/WordPress-Coding-Standards/issues/2035#issuecomment-1325532520

Bethlehem answered 14/12, 2022 at 7:24 Comment(0)
E
1

We recently updated from php 7.4 to 8.1 (on Magento 2.4.4). We also experienced lots of exceptions thrown for deprecation warnings within vendor modules and our extensive in-house custom modules.

We remediated everything we could find, however to be safe, we implemented the following patch, which modifies magento's error handler to log these deprecation warnings, rather than throwing them as exceptions.

On go-live in production, this kept the application running (by simply logging these errrors), and gave us breathing room to address them as we saw them occuring in the logs.

Keep in mind that magento cli does not use this error handler, but rather the symphony framework one, which luckly handles deprecation warnings by outputing them to the console (so watch for those as well).

Upgrade went smooth, so maybe this will helps others...

Index: vendor/magento/framework/App/Bootstrap.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/vendor/magento/framework/App/Bootstrap.php b/vendor/magento/framework/App/Bootstrap.php
--- a/vendor/magento/framework/App/Bootstrap.php
+++ b/vendor/magento/framework/App/Bootstrap.php    (date 1679064575518)
@@ -384,7 +384,7 @@
      */
     private function initErrorHandler()
     {
-        $handler = new ErrorHandler();
+        $handler = new ErrorHandler($this->objectManager->get(LoggerInterface::class));
         set_error_handler([$handler, 'handler']);
     }


Index: vendor/magento/framework/App/ErrorHandler.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/vendor/magento/framework/App/ErrorHandler.php b/vendor/magento/framework/App/ErrorHandler.php
--- a/vendor/magento/framework/App/ErrorHandler.php
+++ b/vendor/magento/framework/App/ErrorHandler.php (date 1679073448870)
@@ -34,6 +34,15 @@
         E_USER_DEPRECATED => 'User Deprecated Functionality',
     ];

+    private $logger;
+
+    public function __construct(
+       $logger = null
+    )
+    {
+        $this->logger = $logger;
+    }
+
     /**
      * Custom error handler
      *
@@ -50,6 +59,12 @@
             // there's no way to distinguish between caught system exceptions and warnings
             return false;
         }
+
+        if (E_DEPRECATED == $errorNo) {
+            $msg = "Logging - PHP Deprecation Warning: {$errorStr} in {$errorFile} on line {$errorLine}";
+            if ($this->logger) $this->logger->warning($msg);
+            return false;
+        }

         $errorNo = $errorNo & error_reporting();
         if ($errorNo == 0) {
Ethaethan answered 30/3, 2023 at 13:18 Comment(0)
D
0

The problem was occured at vendor/laravel/framework/src/Illuminate/Routing/RouteGroup.php:65

You can fix this problem by casting the variable to a string using (string)

Like before it was trim($old, '/') After casting trim((string)$old, '/')

protected static function formatPrefix($new, $old, $prependExistingPrefix = true)
{
    $old = $old['prefix'] ?? null;

    if ($prependExistingPrefix) {
        return isset($new['prefix']) ? trim((string)$old, '/').'/'.trim((string)$new['prefix'], '/') : $old;
    } else {
        return isset($new['prefix']) ? trim((string)$new['prefix'], '/').'/'.trim((string)$old, '/') : $old;
    }
} 
Dupont answered 18/11, 2022 at 18:4 Comment(0)
I
0

Another option is to create a phpFunctionWrapper class that you can inject through the constructor of your class. The wrapper functions should take care of the null coalescent operator rather than introduce this dependency in the code.

For example:

<?php

namespace Vendor\Core\Helper;

class PhpFunctionWrapper
{
    public function numberFormat($number, $decimals): string|false {
        return number_format($number ?? 0.0, $decimals);
    }

    public function strPos($haystack, $needle, int $offset = 0): int|false {
        return strpos( $haystack ?? "", $needle ?? "", $offset);
    }

    public function pregSplit($pattern, $subject, $limit = -1, $flags = 0): array|bool
    {
        return preg_split($pattern ?? '', $subject ?? '', $limit, $flags);
    }

    public function explode($separator, $string, $limit = PHP_INT_MAX): array
    {
        return explode($separator, $string, $limit);
    }
}

Then, you inject the wrapper class in your class through the constructor:

<?php

namespace Vendor\Catalog\Block\Product;

use Vendor\Core\Helper\PhpFunctionWrapper;
use Magento\Catalog\Block\Product\Context;
use Magento\Catalog\Api\ProductRepositoryInterface;

class View extends \Magento\Catalog\Block\Product\View
{
    private PhpFunctionWrapper $phpFunctionWrapper;

    public function __construct(Context                                             $context,
                                \Magento\Framework\Url\EncoderInterface             $urlEncoder,
                                \Magento\Framework\Json\EncoderInterface            $jsonEncoder,
                                \Magento\Framework\Stdlib\StringUtils               $string,
                                \Magento\Catalog\Helper\Product                     $productHelper,
                                \Magento\Catalog\Model\ProductTypes\ConfigInterface $productTypeConfig,
                                \Magento\Framework\Locale\FormatInterface           $localeFormat,
                                \Magento\Customer\Model\Session                     $customerSession,
                                ProductRepositoryInterface                          $productRepository,
                                \Magento\Framework\Pricing\PriceCurrencyInterface   $priceCurrency,
                                PhpFunctionWrapper                                  $phpFunctionWrapper,
                                array                                               $data = [])
    {
        parent::__construct($context,
            $urlEncoder,
            $jsonEncoder,
            $string,
            $productHelper,
            $productTypeConfig,
            $localeFormat,
            $customerSession,
            $productRepository,
            $priceCurrency,
            $data);
        $this->phpFunctionWrapper = $phpFunctionWrapper;
    }
}

Finally, in for example a template file that uses the View block, you change the code from:

<div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $position : '' ?>>

to:

<div data-role="add-to-links" class="actions-secondary"<?= $block->phpFunctionWrapper->strPos($pos, $viewMode . '-secondary') ? $position : '' ?>>

Of course, you need to find all occurrences, but you need to go through them anyway. At least in the future, if you need to change something about these functions, you only need to change the wrapper.

I have created a core helper module where I keep these types of solutions that I can inject where needed. It keeps my code clean, and free from dependencies.

Illsuited answered 21/11, 2022 at 11:22 Comment(0)
G
0

After searching a lot and trying too many solutions, here is the final solution for this issue until they release a newer compatible version of Smarty templates... Add to the sitewide function.php file.

function custom_trim(?string $value) {
    return empty($value) ? '' : trim($value);
}
Gait answered 6/1, 2024 at 11:40 Comment(0)
C
0

While ?? is cool I think doing is_string($val) check might be more appropriate.

You can use regexp replacement to change all occurrences (or at least all typical occurrences).

Find and replace:

  • htmlspecialchars\((\$\w+)\)
  • (!is_string($1) ? '' : htmlspecialchars($1))

This would replace htmlspecialchars($val); with (!is_string($val) ? '' : htmlspecialchars($val)).

Camel answered 31/1, 2024 at 13:51 Comment(0)
R
-1

Well, this deprecated thingy can occur and when trying to return data from empty (NULL) database columns ¯\_(ツ)_/¯

For example this code where $lob is a picture saved as SQLite LOB datatype:

$finfo    = new finfo(FILEINFO_MIME);
$mimeType = $finfo->buffer($lob);
Rhymester answered 2/9, 2022 at 22:24 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.