Problem/Motivation

In PHP 8.0 imagecreatefrom* functions no longer return a resource. They return a \GdImage object.

Proposed resolution

Change \Drupal\system\Plugin\ImageToolkit\GDToolkit to support both a resource and a \GdImage object.

See https://github.com/php/php-src/blob/master/UPGRADING#L249

I think this issue will be okay for PHP 7.0 because it's really only loosening what we expect in \Drupal\system\Plugin\ImageToolkit\GDToolkit::$resource but testing this will be harder without all fixes from #3156595: Make Drupal 9 installable on PHP8

Remaining tasks

User interface changes

None

API changes

\Drupal\system\Plugin\ImageToolkit\GDToolkit::getResource() will return different things depending on PHP version.
\Drupal\system\Plugin\ImageToolkit\GDToolkit::setResource() will expect different things depending on PHP version.

Data model changes

None

Release notes snippet

@todo

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Status: Active » Needs review
StatusFileSize
new2.11 KB

Here's what worked on #3156595: Make Drupal 9 installable on PHP8 but it needs work to tighten up the API.

alexpott’s picture

Issue summary: View changes
mondrake’s picture

What should be done in the __destructor of the toolkit then? Is imagedestroy relevant at all in PHP 8?

alexpott’s picture

@mondrake that's a good question. I don't know the answer to that. Well now I do! https://github.com/php/php-src/blob/92c4b0651368a4135f692af48f4716c96de4...

/* {{{ proto bool imagedestroy(resource im)
   Destroy an image - No effect as of PHP 8.0 */
PHP_FUNCTION(imagedestroy)
{
	/* This function used to free the resource, as resources are no longer used, it does nothing */
	zval *IM;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &IM, gd_image_ce) == FAILURE) {
		RETURN_THROWS();
	}

	RETURN_TRUE;
}
andypost’s picture

StatusFileSize
new1.03 KB
new2.6 KB

Added type-hints

mondrake’s picture

StatusFileSize
new6.42 KB
new5.19 KB

The purpose of testResourceDestruction() is to check that once Drupal's Image objects are losing scope, the memory allocated to the image is cleaned up. See #2531570: Image objects should release memory when losing scope.

The test would still be useful, so I changed it to test memory allocated at various stages.

ayesh’s picture

`is_resource()` can accept any data type, but PHP 7.2 and older versions will fatal error on `instanceOf` checks. So for PHP 7.2 and older version compatibility, we will need a `is_object()` check too: https://php.watch/versions/8.0/gdimage#gdimage-is-resource

Further, `imagedestroy()` function is still available in PHP 8. I think we can simply call `imagedestroy()` without any additional checks because PHP internally no-ops it anyway.

andypost’s picture

StatusFileSize
new3.52 KB
new6.41 KB

Confirm as no-op https://github.com/php/php-src/blob/master/ext/gd/gd.c#L2040-L2052

Fixed check as suggested #8 if we need to backport it to PHP < 7.3 https://3v4l.org/lJnKN
Also replaced deprecated 'toInt()' usage for Bytes

Now it needs follow-up to finish @todo

mondrake’s picture

+++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
@@ -100,6 +100,8 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
+    // @TODO Remove this method when core start require PHP 8.0 as imagedestroy
+    //   is no longer needed.

this @todo needs to be moved above into the docblock of the method.

+++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
@@ -427,31 +428,59 @@ public function testManipulations() {
+    $preload_memory_usage = memory_get_usage(TRUE);

it would be nicer to use the service provided by #3000050: Replace \Drupal\Component\Utility\Environment::checkMemoryLimit with a PHP memory checking helper class and service but, alas, that is still NR.

  1. +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
    @@ -124,15 +126,17 @@ public static function create(ContainerInterface $container, array $configuratio
    +    if (!(is_object($resource) && $resource instanceof \GdImage)) {
    +      if (!is_resource($resource) || get_resource_type($resource) != 'gd') {
    +        throw new \InvalidArgumentException('Invalid resource argument');
    +      }
    

    could use some comments

  2. +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
    @@ -142,11 +146,11 @@ public function setResource($resource) {
    +    if (!(is_resource($this->resource) || (is_object($this->resource) && $this->resource instanceof \GdImage))) {
    

    same

andypost’s picture

StatusFileSize
new1.72 KB
new6.63 KB

Filed follow-up #3173031: Clean-up \Drupal\system\Plugin\ImageToolkit\GDToolkit when core require php 8.0

Did not change #3000050 because checkMemoryLimit() is not getting current usage, will dig this issue later

mondrake’s picture

RTBC +1 but I cannot. Thanks @andypost

andypost’s picture

+++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
@@ -427,31 +428,59 @@ public function testManipulations() {
+    $preload_memory_usage = memory_get_usage(TRUE);
...
+    $postload_memory_usage = memory_get_usage(TRUE);
...
+    $postdestroy_memory_usage = memory_get_usage(TRUE);
...
+    $postload_memory_usage = memory_get_usage(TRUE);
...
+    $postcreatenew_memory_usage = memory_get_usage(TRUE);
+    $this->assertLessThan($postload_memory_usage - Bytes::toNumber('100 MB'), $postcreatenew_memory_usage);

it could use EnvironmentMemory::getUsed() but not sure it better or more intuitive then one function call? ref #3000050-43: Replace \Drupal\Component\Utility\Environment::checkMemoryLimit with a PHP memory checking helper class and service
Maybe it more useful to get limits...

alexpott’s picture

Status: Needs review » Needs work

I'm not a huge fan of the memory usage testing. It looks fragile and the type of thing that'll fail when PHP makes memory optimisations or changes to garbage collection. I really don't think it is in scope to add this here and should be in follow-up were we can discuss. This would the first instance of testing memory usage like this in core. Also I'm not 100% convinced the changes added here will pass on PHP 8 - again due to the vagaries of garbage collection.

gábor hojtsy’s picture

Title: Add support for \GdImage objects » \Drupal\system\Plugin\ImageToolkit\GDToolkit needs to support \GdImage objects for PHP 8 compatibility

Retitling so it does not sound like a feature request :)

alexpott’s picture

The current code introduces some coding standards errors

core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:449:6 - Unknown word (postload)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:450:82 - Unknown word (postload)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:455:6 - Unknown word (postdestroy)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:456:77 - Unknown word (postdestroy)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:467:6 - Unknown word (postload)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:474:6 - Unknown word (postcreatenew)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:475:28 - Unknown word (postload)
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php:475:80 - Unknown word (postcreatenew)
FILE: core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 438 | ERROR | String concat is not required here; use a single
     |       | string instead
----------------------------------------------------------------------
andypost’s picture

Status: Needs work » Needs review
StatusFileSize
new4.36 KB
new3.57 KB

This test makes sense only while GD using resources, so I reverted test change to #2 and added another todo

PS patch smaller then interdiff

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
@@ -2,6 +2,7 @@
+use Drupal\Component\Utility\Bytes;

Unused use.

Also https://www.drupal.org/pift-ci-job/1836885 shows that the memory testing introduced by earlier patches would not have worked.

ayushmishra206’s picture

Status: Needs work » Needs review
StatusFileSize
new528 bytes
new3.35 KB

Made the changes pointed out in #18. Please review.

mondrake’s picture

Issue tags: +Needs followup

#14 IMO we should have a followup opened and referred to in the comment. I see the point in #14, but the root cause for entire test to be developed was memory management issues that were addressed in #2531570: Image objects should release memory when losing scope. The memory was filled up by not gc'ed resources, and the solution was to destroy the resources in __destruct. Now we will be losing test coverage of the root cause.

andypost’s picture

There's test which makes sense only for php7, personally instead of measuring RAM I'd converted test to unit-test to make sure destructor executes resource destroy.
Measuring memory is bad idea because it's PHP GC territory to decide when and how reclaim RAM

There's follow-up already to get rid of method and test, so could be used to improve test

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

andypost’s picture

Issue tags: -Needs followup
StatusFileSize
new671 bytes
new3.35 KB

Filed another follow-up #3179058: Remove ToolkitGdTest::testResourceDestruction() when core require PHP 8.0

So clean-up of destructor and fate of the test are separate issues

longwave’s picture

Status: Needs review » Reviewed & tested by the community

Changes look OK as per the PHP 8 upgrade notes. I checked for other uses of is_resource() and there are a few GD related ones but on closer inspection they all relate to destroying images after use so is_resource() alone is still OK and these can be removed when we have PHP 8 as a minimum dependency.

alexpott’s picture

Priority: Normal » Critical

This is a critical because without it we get deprecation notices on PHP 8.0

  • catch committed f1c3914 on 9.2.x
    Issue #3156887 by andypost, mondrake, ayushmishra206, alexpott, Ayesh,...

  • catch committed 95ab9fa on 9.1.x
    Issue #3156887 by andypost, mondrake, ayushmishra206, alexpott, Ayesh,...
catch’s picture

Version: 9.2.x-dev » 9.1.x-dev
Status: Reviewed & tested by the community » Fixed

This looks great as do the follow-ups.

Committed/pushed to 9.2.x and cherry-picked to 9.1.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.