From fb25f332818c1906a63bf3c8412f3df48504bb06 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 11 May 2022 11:23:46 +0300 Subject: [PATCH] Fixed calls to undefined `Media` methods, they will now return `null` in order to fix twig templates with undefined variables --- CHANGELOG.md | 1 + .../Media/Interfaces/MediaObjectInterface.php | 13 +++++-- .../Common/Media/Traits/ImageMediaTrait.php | 28 +++++++++------ .../Common/Media/Traits/MediaObjectTrait.php | 34 +++++++++++-------- .../Grav/Common/Page/Markdown/Excerpts.php | 18 +++++++--- .../Grav/Common/Page/Medium/ImageMedium.php | 2 +- system/src/Grav/Common/Page/Medium/Link.php | 5 ++- 7 files changed, 67 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3a464e18..0407b8f10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Fixed locking and reading files using `Framework\File` classes * Fixed `Utils::resolveTokenPath()` with `@variable@` not working properly * Fixed remote URLs in markdown if using subfolder setup + * Fixed calls to undefined `Media` methods, they will now return `null` in order to fix twig templates with undefined variables # v1.7.33 ## 04/25/2022 diff --git a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php index 851ec7113..950aa4d40 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php @@ -240,14 +240,23 @@ interface MediaObjectInterface extends \Grav\Framework\Media\Interfaces\MediaObj public function style(string $style); /** - * Allow any action to be called on this medium from twig or markdown + * Checks if the action is supported by the media object. * * @param string $method + * @return bool + * @phpstan-pure + */ + public function isAction(string $method): bool; + + /** + * Adds query string to the media object. + * + * @param string $var * @param array $args * @return $this * @phpstan-impure */ - public function __call(string $method, array $args); + public function addQuerystring(string $var, array $args); /** * Get value by using dot notation for nested arrays/objects. diff --git a/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php b/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php index b3c0ca57b..51db194a5 100644 --- a/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php @@ -36,8 +36,9 @@ trait ImageMediaTrait public static array $magic_actions = [ 'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop', 'negate', 'brightness', 'contrast', 'grayscale', 'emboss', - 'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive', - 'rotate', 'flip', 'fixOrientation', 'gaussianBlur', 'format', 'create', 'fill', 'merge' + 'smooth', 'sharp', 'edge', 'colorize', 'sepia', + 'enableProgressive', 'rotate', 'flip', 'fixOrientation', 'gaussianBlur', + 'format', 'create', 'fill', 'merge' ]; /** @var array */ @@ -471,9 +472,7 @@ trait ImageMediaTrait */ public function cropZoom(...$args) { - $this->__call('zoomCrop', $args); - - return $this; + return $this->zoomCrop(...$args); } /** @@ -552,7 +551,7 @@ trait ImageMediaTrait break; } - $this->__call('merge', [$watermark, $positionX, $positionY]); + $this->merge($watermark, $positionX, $positionY); return $this; } @@ -579,11 +578,11 @@ trait ImageMediaTrait $frame = ImageFile::create($dst_width, $dst_height); - $frame->__call('fill', [$color]); + $frame->fill($color); $this->image = $frame; - $this->__call('merge', [$image, $border, $border]); + $this->merge($image, $border, $border); $this->saveImage(); */ @@ -591,6 +590,15 @@ trait ImageMediaTrait return $this; } + /** + * @param string $method + * @return bool + */ + public function isAction(string $method): bool + { + return in_array($method, static::$magic_actions, true) || parent::isAction($method); + } + /** * Forward the call to the image processing method. * @@ -602,7 +610,7 @@ trait ImageMediaTrait public function __call(string $method, array $args) { if (!in_array($method, static::$magic_actions, true)) { - return parent::__call($method, $args); + return null; } // Always initialize image. @@ -628,7 +636,7 @@ trait ImageMediaTrait } // Do the same call for alternative media. - $medium->__call($method, $args_copy); + $medium->{$method}(...$args_copy); } } catch (BadFunctionCallException $e) { } diff --git a/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php b/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php index 7f0cc872c..2339bf3b5 100644 --- a/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php @@ -11,7 +11,6 @@ namespace Grav\Common\Media\Traits; use Grav\Common\Data\Data; use Grav\Common\Media\Interfaces\ImageMediaInterface; -use Grav\Common\Media\Interfaces\MediaFileInterface; use Grav\Common\Media\Interfaces\MediaLinkInterface; use Grav\Common\Media\Interfaces\MediaObjectInterface; use Grav\Common\Page\Medium\Medium; @@ -574,27 +573,34 @@ trait MediaObjectTrait } /** - * Allow any action to be called on this medium from twig or markdown - * * @param string $method - * @param array $args - * @return MediaObjectInterface|MediaLinkInterface + * @return bool */ - public function __call(string $method, array $args) + public function isAction(string $method): bool + { + return false; + } + + /** + * @param string $var + * @param array $args + * @return static + */ + public function addQuerystring(string $var, array $args) { $count = count($args); if ($count > 1 || ($count === 1 && !empty($args[0]))) { - $method .= '=' . implode(',', array_map(static function ($a) { - if (is_array($a)) { - $a = '[' . implode(',', $a) . ']'; - } + $var .= '=' . implode(',', array_map(static function ($a) { + if (is_array($a)) { + $a = '[' . implode(',', $a) . ']'; + } - return rawurlencode($a); - }, $args)); + return rawurlencode($a); + }, $args)); } - if (!empty($method)) { - $this->querystring($this->querystring(null, false) . '&' . $method); + if (!empty($var)) { + $this->querystring($this->querystring(null, false) . '&' . $var); } return $this; diff --git a/system/src/Grav/Common/Page/Markdown/Excerpts.php b/system/src/Grav/Common/Page/Markdown/Excerpts.php index c886b3319..f11f3068d 100644 --- a/system/src/Grav/Common/Page/Markdown/Excerpts.php +++ b/system/src/Grav/Common/Page/Markdown/Excerpts.php @@ -19,11 +19,11 @@ use Grav\Common\Utils; use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use function array_key_exists; -use function call_user_func_array; use function count; use function dirname; use function in_array; use function is_bool; +use function is_callable; use function is_string; /** @@ -282,7 +282,7 @@ class Excerpts $actions = []; - // if there is a query, then parse it and build action calls + // if there is a query, then parse it and build action calls. if (isset($url_parts['query'])) { $actions = array_reduce( explode('&', $url_parts['query']), @@ -297,6 +297,7 @@ class Excerpts ); } + // Add default actions. $defaults = $this->config['images']['defaults'] ?? []; if (count($defaults)) { foreach ($defaults as $method => $params) { @@ -313,13 +314,22 @@ class Excerpts foreach ($actions as $action) { $matches = []; - if (preg_match('/\[(.*)\]/', $action['params'], $matches)) { + if (preg_match('/\[(.*)]/', $action['params'], $matches)) { $args = [explode(',', $matches[1])]; } else { $args = explode(',', $action['params']); } - $medium = call_user_func_array([$medium, $action['method']], $args); + $method = $action['method']; + $result = null; + if (is_callable([$medium, $method]) || $medium->isAction($method)) { + $result = $medium->{$method}(...$args); + } + if ($result instanceof Medium) { + $medium = $result; + } else { + $medium->addQuerystring($method, $args); + } } if (isset($url_parts['fragment'])) { diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index 812c912d7..0081ab109 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -335,7 +335,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate } if ($width && $height) { - $this->__call('cropResize', [$width, $height]); + $this->cropResize($width, $height); } return parent::lightbox($width, $height, $reset); diff --git a/system/src/Grav/Common/Page/Medium/Link.php b/system/src/Grav/Common/Page/Medium/Link.php index fef3f7124..c9e306530 100644 --- a/system/src/Grav/Common/Page/Medium/Link.php +++ b/system/src/Grav/Common/Page/Medium/Link.php @@ -105,12 +105,11 @@ class Link implements RenderableInterface, MediaLinkInterface public function __call(string $method, array $args) { $object = $this->source; - $callable = [$object, $method]; - if (!is_callable($callable)) { + if (!(is_callable([$object, $method]) || $object->isAction($method))) { throw new BadMethodCallException(get_class($object) . '::' . $method . '() not found.'); } - $object = call_user_func_array($callable, $args); + $object = $object->{$method}(...$args); if (!$object instanceof MediaLinkInterface) { // Don't start nesting links, if user has multiple link calls in his // actions, we will drop the previous links.