[wp-trac] [WordPress Trac] #42663: Imagick support for stream wrappers

WordPress Trac noreply at wordpress.org
Sat Oct 17 01:22:27 UTC 2020


#42663: Imagick support for stream wrappers
-------------------------------------------------+-------------------------
 Reporter:  calin                                |       Owner:
                                                 |  mikeschroder
     Type:  enhancement                          |      Status:  assigned
 Priority:  normal                               |   Milestone:  5.6
Component:  Media                                |     Version:
 Severity:  normal                               |  Resolution:
 Keywords:  has-patch 2nd-opinion has-unit-      |     Focuses:
  tests                                          |
-------------------------------------------------+-------------------------

Comment (by p00ya):

 So I wrote something that went through `make_image`, slept on it, then
 decided not to anyway.  See recent commits on https://github.com/WordPress
 /wordpress-develop/pull/521 .

 TLDR: I've factored out the stream vs file logic into a new, **private**
 `_write_image` method, which takes care of the separation-of-concerns in a
 much cleaner way than using `make_image`.  Clients relying on `make_image`
 will retain the existing behaviour.

 Long post explaining my reasoning follows.

 I think it's a useful exercise to try and define exactly what
 `make_image`'s contract is.  I'll expand on the definition I gave before
 for `WP_Image_Editor`:

  If $filename is a file, then just call $function($arguments...), which
 should write an image to $filename. If $filename is a stream URL, call
 $function($arguments...). $function should output an image, and make_image
 will take care of capturing it in a buffer and then writing it to
 $filename.

 Already we can see this is a bit awkward when considering separation of
 concerns: ''sometimes'' `make_image` offers to write to the destination,
 but other times $function must take care of writing to the destination.

 Now lets examine the contract for `WP_Image_Editor_GD`:

  If $filename is a file, then just call $function($arguments...), which
 should write an image to $filename.  If $filename is a stream URL, call
 $function($arguments...), but actually replace `$arguments[1]` with NULL.
 $function should output an image, and make_image will take care of
 capturing it in a buffer and then writing it to $filename.

 This is obviously getting awkward.  Is `WP_Image_Editor_GD::make_image`
 even polymorphic when it subtly changes the contract from the parent by
 poking at the callback arguments?  This is also terrible from a
 readability perspective: the reader has to check the implementations of
 `WP_Image_Editor_GD::make_image`, `WP_Image_Editor::make_image` and
 `imagegif` to figure out WTF is going on in a call like:

 {{{#!php
 $this->make_image( $filename, 'imagegif', array( $image, $filename ) )
 }}}

 Now lets talk about compatibility.  `make_image` is protected, so the only
 clients we need to be concerned with are those in wordpress-core or third
 party plugins that are **subclassing** `WP_Image_Editor_Imagick`.  There's
 no subclasses in wordpress-core (and even if there were, I'd fix them).

 So if I were such a plugin developer, I'd probably be trying something
 like:

 {{{#!php

 class Bestest_Image_Editor extends WP_Image_Editor_Imagick {

 // ...

 if ( wp_is_stream ( $filename ) ) {
   // Thanks, make_image, for the handy output buffering.
   $this->make_image( $filename, array( $this, 'stream' ), array( ) );
 } else {
   // Why even bother with make_image here?
   $this->image->writeImage( $filename );
 }
 }}}

 This probably works today.  As a side-note, it demonstrates that nobody
 can trust `make_image` to truly abstract the file/URL distinction; the
 caller already needs to be concerned with passing different callbacks in,
 so `make_image` just becomes a utility for output buffering.  At this
 point, the arguments in my previous post apply.

 There's another possibility to consider for compatibility: clients are
 viewing `make_image` as some kind of template method design pattern, and
 overriding it.  If we don't call it, their important image-writing hook
 will not fire.  I think this scenario is very unlikely, and I'm also not
 sympathetic to these clients (since they're relying on undocumented
 implementation details of the `_save` method).

 So if we want to use `make_image`, we either need to override it like GD
 (which breaks polymorphic contracts and therefore code) or submit to
 output buffering (breaks separation-of-concerns anyway, and my previous
 concerns about readability and memory usage apply).

 So I'd still prefer to view `make_image` simply as an output buffering
 utility for image editor subclasses, and never even use it from
 `WP_Image_Editor_Imagick`.  It has an unfortunate API contract, but folks
 might already be relying on it and GD found a way to use it so whatevs.

 If it's just a protected helper function, lets not be obsessed with
 awkwardly calling it when there are much more direct calls.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/42663#comment:31>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list