backdrop-issues icon indicating copy to clipboard operation
backdrop-issues copied to clipboard

PHP 8.4 Support

Open quicksketch opened this issue 1 year ago • 29 comments

Description of the bug

PHP 8.4 was released last month and continues to tighten the screws on less-than-perfect PHP code. Immediately upon switching to PHP 8.4, the homepage of Backdrop has the following errors:

Deprecated: Constant E_STRICT is deprecated in /var/www/html/core/includes/bootstrap.inc on line 742

Deprecated: Constant E_STRICT is deprecated in /var/www/html/core/includes/errors.inc on line 28

Deprecated: backdrop_get_query_parameters(): Implicitly marking parameter $query as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/common.inc on line 497

Deprecated: _format_date_callback(): Implicitly marking parameter $matches as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/common.inc on line 2541

Deprecated: actions_execute(): Implicitly marking parameter $entity as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/actions.inc on line 85

Deprecated: comment_access(): Implicitly marking parameter $comment as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/comment/comment.module on line 1513

Deprecated: entity_access(): Implicitly marking parameter $entity as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.module on line 191

Deprecated: entity_access(): Implicitly marking parameter $account as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.module on line 191

Deprecated: entity_build_content(): Implicitly marking parameter $entity as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.module on line 225

Deprecated: taxonomy_vocabulary_static_reset(): Implicitly marking parameter $ids as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/taxonomy/taxonomy.module on line 853

Deprecated: user_has_role(): Implicitly marking parameter $account as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/user/user.module on line 576

Deprecated: UpdateQuery::expression(): Implicitly marking parameter $arguments as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/query.inc on line 1157

Deprecated: MergeQuery::expression(): Implicitly marking parameter $arguments as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/query.inc on line 1420

Deprecated: SelectQueryInterface::getArguments(): Implicitly marking parameter $queryPlaceholder as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/select.inc on line 177

Deprecated: SelectQueryInterface::preExecute(): Implicitly marking parameter $query as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/select.inc on line 505

Deprecated: SelectQueryExtender::getArguments(): Implicitly marking parameter $queryPlaceholder as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/select.inc on line 812

Deprecated: SelectQueryExtender::preExecute(): Implicitly marking parameter $query as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/select.inc on line 826

Deprecated: SelectQuery::getArguments(): Implicitly marking parameter $queryPlaceholder as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/select.inc on line 1549

Deprecated: SelectQuery::preExecute(): Implicitly marking parameter $query as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/includes/database/select.inc on line 1567

Deprecated: EntityControllerInterface::resetCache(): Implicitly marking parameter $ids as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.controller.inc on line 27

Deprecated: EntityControllerInterface::resetStaticCache(): Implicitly marking parameter $ids as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.controller.inc on line 36

Deprecated: DefaultEntityController::resetCache(): Implicitly marking parameter $ids as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.controller.inc on line 217

Deprecated: DefaultEntityController::resetStaticCache(): Implicitly marking parameter $ids as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/entity/entity.controller.inc on line 235

Deprecated: system_menu_tree_prune_tree(): Implicitly marking parameter $parent_item as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/core/modules/system/system.menu.inc on line 370

There are probably many other situations where this occurs. To fix this, our goal should be to bump the test runners from PHP 8.3 to 8.4 and have 100% tests passing.

Steps To Reproduce

To reproduce the behavior:

  1. Install Backdrop normally on PHP 8.3 within DDEV.
  2. Edit the .ddev/config.yml file and change the php line to php: 8.4
  3. Restart DDEV to take effect ddev restart
  4. Visit the homepage, i.e. https://backdrop.ddev.site

Additional information

Corresponding Drupal 10/11 issue is here: https://www.drupal.org/project/drupal/issues/3427903 Corresponding Drupal 7 issue: ?? There may not be one

Add any other information that could help, such as:

  • Backdrop CMS version: 1.x (1.30.x)
  • Web server and its version: Apache 2.4.62
  • PHP version: 8.4.1
  • Database sever (MySQL or MariaDB?) and its version: 10.11.10
  • Operating System and its version: Ubuntu 24.04
  • Browser(s) and their versions: all

quicksketch avatar Dec 14 '24 01:12 quicksketch

Initial PR filed at https://github.com/backdrop/backdrop/pull/4930 to run the tests.

quicksketch avatar Dec 14 '24 02:12 quicksketch

So we have to make them not nullable, or make the minimum version PHP 7.1? https://wiki.php.net/rfc/deprecate-implicitly-nullable-types. Seems like the explicit nullable assignment was only introduced in 7.1.

herbdool avatar Dec 14 '24 02:12 herbdool

Implicitly marking parameter $ids as nullable is deprecated, the explicit nullable type must be used instead

This problem (and all the similar ones) aren't easy to solve. For a function declaration like this: function foo(array $bar = NULL), we have the following options:

  • Make all parameters with a type not have a default of = NULL (that's what my PR started doing initially): function foo(array $bar = array())
  • Remove the type from the parameter: function foo($bar = NULL).
  • Bump the minimum PHP version to 7.1 and declare nullable with ?: function foo(?array $bar)

Unfortunately both the first and second options will cause API breaks, especially where classes have declared interfaces or base classes as types in their method declarations. Removing the type cast causes an API break because PHP 7.2 requires declarations to be identical between parent and child classes.

It may be that our only option for PHP 8.4 support is to drop support for PHP 7.1 and below? It does look like about 2% of sites are on those older versions.

image

quicksketch avatar Dec 14 '24 02:12 quicksketch

Here's the corresponding issue for Drupal: https://www.drupal.org/project/drupal/issues/3427903 but it's only for Drupal 10/11. Considering the Drupal 7 EOL is next month, PHP 8.3 may be the last version of PHP that can run Drupal 7? I'm curious what the LTS support vendors are going to do about this situation.

quicksketch avatar Dec 14 '24 03:12 quicksketch

I looked to see what WordPress did. There's a thread here https://core.trac.wordpress.org/ticket/60786. There are new commits which declare nullable. And they have also bumped up the minimum version to 7.2. https://core.trac.wordpress.org/ticket/58719#comment:48, I think for other reasons.

herbdool avatar Dec 14 '24 05:12 herbdool

Hm. We knew, something like this would happen at some point. But it still feels earlier than expected. :wink:

So, let's start evaluating: Backdrop core on PHP 8.4 works. What I do get on every page:

Deprecated function: session_set_save_handler(): Providing individual callbacks instead of an object implementing SessionHandlerInterface is deprecated in backdrop_session_initialize() (line 248 of ...core/includes/session.inc).

But none of the other nagging, actually.

There is nagging about Constant E_STRICT in php logs, but that's it.

I installed PHP 8.4.1 on Debian Bookworm via Sury repo, and I'm testing with the latest Backdrop dev. Logging and errors fully turned on (admin/config/development/logging).

Out of curiosity I ran one functional test (I can't run them all at once on my dev box). "EntityAPITestCase" runs with 20 passes, 0 fails, 1 exception, and 1 debug message. The exception is caused by the session_set_save_handler().

So, it might be that Debian (Sury) compiled PHP differently than DDev, or maybe it depends on the version? According to php.net version 8.4.1 is the latest.

indigoxela avatar Dec 14 '24 08:12 indigoxela

Before starting to put actual work in some PR, it might make sense to first evaluate our options.

Looking at the rfc I'm seriously considering to drop PHP 7.0 and below, and go with Nullable Types syntax.

But doesn't that deprecation mean that we only have to take care of params with type declaration like array $foobar = NULL but don't have to change anything if we stick with $foobar = NULL ( :point_left: no type hint)?

If my current assumption is wrong (which might be the case), and we really have to crawl through all of our code to fix function params, I'm leaning towards dropping support for PHP 7.0 and below. I'm aware, this would be a big step. And for sure, that's no topic for Backdrop 1.30.

If we only have to do some smaller tweaks for now (like with session_set_save_handler) it might eventually be a topic for 1.30.

indigoxela avatar Dec 14 '24 09:12 indigoxela

But doesn't that deprecation mean that we only have to take care of params with type declaration like array $foobar = NULL but don't have to change anything if we stick with $foobar = NULL ( 👈 no type hint)?

That's correct. If there's no type hint, there's no problem. The scope of this change is limited to where type hints are in use, which is probably in ~100 places but it's not literally everywhere. We have = NULL all over the place for strings and integers, but fortunately we have no type hints on those because they weren't PHP 5.x compatible. The only place we see this is where we're passing objects or arrays as parameters that accept a NULL default.

Deprecated function: session_set_save_handler(): Providing individual callbacks instead of an object implementing SessionHandlerInterface is deprecated in backdrop_session_initialize() (line 248 of ...core/includes/session.inc).

I hadn't realized this change. But honestly I think this is a good thing. session.inc using individual functions instead of a session object has been sort of ridiculous. It's been an available option for a very long time. We should check with the contrib Redis module to see if we can switch it to objects. I don't think the memcache module for Backdrop (which I maintain) ever had session ability.

quicksketch avatar Dec 14 '24 09:12 quicksketch

If there's no type hint, there's no problem. The scope of this change is limited to where type hints are in use, which is probably in ~100 places

Probably less... Hard to know, as all the tools (phpcs, php-cs-fixer...) don't seem to be ready yet, to search for PHP 8.4 deprecations.

Playing with old-school grep: grep -r '(.*[a-zA-Z]\+ $[a-z_]\+ = NULL' - that finds 31 lines. Looks sort of feasible for Backdrop 1.31.

We might be able to prevent raising minimal PHP version by dropping type hints, right? Because how could we fix something like Layout $layout = NULL without removing the "Layout" hint here?

indigoxela avatar Dec 14 '24 10:12 indigoxela

We might be able to prevent raising minimal PHP version by dropping type hints, right?

Except that dropping type hints affects parent/child definitions in classes, and so any child classes in contrib would start throwing errors in PHP 7.2+ by removing type hints. Though I am not clear on whether ?Layout $layout is compatible with Layout $layout = NULL defined by child classes. I hope PHP granted us this grace.

quicksketch avatar Dec 14 '24 10:12 quicksketch

Except that dropping type hints affects parent/child definitions in classes...

Ouch... right. :thinking: Food for thought after 1.30 is out.

indigoxela avatar Dec 14 '24 10:12 indigoxela

I didn't see this mentioned above: the other problem with dropping the type hints is that it makes them optional, rather than implicitly nullable. And they would have to be the last parameter, which might not be the case.

herbdool avatar Dec 14 '24 13:12 herbdool

For context, CiviCRM is dropping support for PHP 7.4 https://civicrm.org/blog/eileen/php-74-going-end-life-php-84-ramping.

herbdool avatar Dec 23 '24 22:12 herbdool

I did a little digging re our deprecated usage of session_set_save_handler() (in core/includes/session.inc).

We'll have to implement SessionHandlerInterface, but we might benefit from return type declarations, which have been added in PHP 7.0. Otherwise we end up with 6 more #[\ReturnTypeWillChange]. :wink: (We currently have 20 of those in core.)

To clarify the difference (class BackdropSessionHandler implements SessionHandlerInterface):

Also works in PHP 5.6:

  #[\ReturnTypeWillChange]
  public function open($savePath, $sessionName) {
    return _backdrop_session_open();
  }

Requires at least PHP 7.0:

  public function open($savePath, $sessionName): bool {
    return _backdrop_session_open();
  }

^^ given, that the return types really are the same (didn't verify that...).

To me this seems like yet another argument for raising the PHP version requirement. If we don't do it now for PHP 8.4 support, we will definitely have to do it when supporting PHP 9.

And I suspect, we might not be able to avoid it with 8.4, as otherwise we run into trouble with some of our functions, for which dropping the type hint isn't an option (optional param would be followed by a required one then...).

An example for that:

function layout_condition_edit_title(Layout $layout = NULL, Block $block = NULL, LayoutMenuItem $menu_item = NULL, $handler_id) {

^^ That situation seems rare, though...

The situation, that dropping a type hint requires to also handle that change in classes extending core classes seems to be quite as rare.

I found EntityControllerInterface::resetCache(), though. And SelectQueryInterface::preExecute(). :thinking:

indigoxela avatar Dec 25 '24 11:12 indigoxela

Though I am not clear on whether ?Layout $layout is compatible with Layout $layout = NULL defined by child classes. I hope PHP granted us this grace.

I tested it and I think we've been granted this grace.

I fixed resetCache(?array $ids) for EntityControllerInterface but left TaxonomyTermController::resetCache(array $ids = NULL). Then I cleared the cache. The depreciation warning appears but no errors.

herbdool avatar Jan 19 '25 04:01 herbdool

strtok() is deprecated. I had just noticed it is used in a few places. https://wiki.php.net/rfc/deprecations_php_8_4. Though oddly, it's not mentioned here https://www.php.net/strtok.

Update: Turns out that page was just the proposals and strtok deprecation did not pass.

herbdool avatar Jan 19 '25 04:01 herbdool

We should check with the contrib Redis module to see if we can switch it to objects.

The Redis module doesn't have capability for storing sessions. https://github.com/backdrop-contrib/redis/commit/ac11b29dc935a2f0dd65c3c15024274bbf7f16ba and https://www.drupal.org/node/2752735

herbdool avatar Jan 19 '25 05:01 herbdool

I tested it and I think we've been granted this grace.

That's good to know.

To summarize: we have to decide to either introduce breaking changes for at least two pretty central methods in core (EntityControllerInterface::resetCache and SelectQueryInterface::preExecute) or we raise PHP minimal requirements to 7.1 to make use of nullable type declarations.

Implicitly nullable parameter types are deprecated as of PHP 8.4. The successor, nullable types have been introduced in 7.1.

We have to rewrite core/includes/session.inc to implement SessionHandlerInterface - which would also be possible in a bc way and could provide support even for PHP 5.6. Something I noticed along the way when experimenting: the "internal" function _backdrop_session_write() is in use in two contrib projects (backup_migrate and d2b_migrate). We have to consider that when rewriting.

IF we decide to raise the min version to 7.1 (hence drop support for PHP 5.6 and 7.0), we'd additionally get the ability to get rid of the \ReturnTypeWillChange in core by using return type declarations, so we'd be prepared for PHP 9 (where return type declarations will probably be enforced). Note that a release date for PHP 9 isn't published yet.

indigoxela avatar Jan 19 '25 08:01 indigoxela

Your link about nullable types in 7.1 is helpful:

Parameter Types

The nullable type cannot be removed in a sub-class; it can be added if not present in a super-class. This behavior is consistent with the Liskov substitution principle.

I'm guessing then that my test worked only because the subclass had implicit nullable. If the subclass removed = NULL then it would produce a fatal error.

It seems like we're leaning towards increasing to minimum PHP 7.1 rather than introduce breaking changes. I believe the PMC will make the final decision based on our recommendation.

herbdool avatar Jan 19 '25 17:01 herbdool

This will need a PMC decision and announcement (we already have an internal placeholder issue for it in the private PMC issue queue - pending final decision/plan/recommendations here).

PS: Would be great if we had a way to tag issues with the need for PMC decision. Should I create one such label?

klonos avatar Jan 19 '25 19:01 klonos

We have to rewrite core/includes/session.inc to implement SessionHandlerInterface - which would also be possible in a bc way and could provide support even for PHP 5.6.

I updated the PR with a class implementation of session handling. For now it simply calls the old functions and leaves everything else in place.

I also am just now realizing that these two syntaxes do not mean the same thing:

function test1(array $optional_array = NULL) {
function test2(?array $optional_array) {

The first function can be called with no parameter at all. But the second function requires NULL be passed in.

test1(NULL); // Works.
test1(array()); // Works.
test1(); // Works.

test2(NULL); // Works.
test2(array()); // Works.
test2(); // Does not work.

So just replacing all of our function/method signatures to include NULLable parameters also does not provide 100% compatibility for existing functions.


EDIT:

NULLable parameters can still have a default set to NULL. So if we do this:

function test3(?array $optional_array = NULL) {

That is still 100% compatible with newer PHP versions.

test3(NULL); // Works.
test3(array()); // Works.
test3(); // Works.

quicksketch avatar Jan 19 '25 19:01 quicksketch

PR at https://github.com/backdrop/backdrop/pull/4930 is ready for review.

With @indigoxela's suggestion, it seems as though we only need to bump to PHP 7.1 (not 7.2), so I set up the PR to work at that minimal increase.

I'm feeling pretty good that this set of changes is safe for sites running 7.1+, though sites that are on PHP 5.6 or 7.0 will be guaranteed not to run, as they will encounter syntax errors: Backdrop won't even be able to return an error page.

quicksketch avatar Jan 19 '25 23:01 quicksketch

Awesome, tests passing!

I'm not sure if the currently used doc block notation's actually correct, though.

And the sandbox throws a deprecated notice on the home page even before logging in:

Deprecated: Optional parameter $entity declared before required parameter $context is implicitly treated as a required parameter in /var/lib/tugboat/core/includes/actions.inc on line 85

^^ sandboxes still run on 8.3, so that problem has nothing to do with 8.4.

More testing soon. We still have time... 😉

indigoxela avatar Jan 20 '25 07:01 indigoxela

This issue is now unblocked because the PMC officially voted and approved dropping PHP 5.6 and 7.1 support. See https://backdropcms.org/news/backdrop-cms-drops-support-for-php-versions-prior-to-71

quicksketch avatar Feb 06 '25 18:02 quicksketch

I set the milestone, as it has already been officially announced.

indigoxela avatar Feb 17 '25 07:02 indigoxela

Re mixing union types and nullable type notation... I belief, I found something (sort of) related here

Union types and the ?T nullable type notation cannot be mixed. Writing ?T1|T2, T1|?T2 or ?(T1|T2) is not supported and T1|T2|null needs to be used instead.

It's not about doc blocks, though, but function signatures (which we can't use, yet).

indigoxela avatar Feb 17 '25 07:02 indigoxela

I've updated the PR to follow the coding standards discussed and addressed all review feedback. All tests are passing and this is ready for review again: https://github.com/backdrop/backdrop/pull/4930

quicksketch avatar Feb 20 '25 18:02 quicksketch

I've updated the PR to follow the coding standards discussed and addressed all review feedback

That's great news. Really close! It almost works, besides the PHP Deprecated: actions_execute() thing, which you forgot to commit.

And I added a handful of questions.

indigoxela avatar Feb 22 '25 08:02 indigoxela

It might be that our phpcs version in our GHA's a bit old. We set the version to 3.7 (fixed), but eventually it might make sense to not set it at all (use that latest). Simply drop the :3.7 in file .github/workflows/phpcs-lint.yml (locally I'm using v3.11.3 without any problems).

indigoxela avatar Feb 23 '25 11:02 indigoxela

I think the PR at https://github.com/backdrop/backdrop/pull/4930 is now ready for review again. Sorry for the delay.

quicksketch avatar Mar 21 '25 05:03 quicksketch