diff --git a/src/Imaging/ImageGenerator.php b/src/Imaging/ImageGenerator.php index e347f0a1224..f5f9e50849c 100644 --- a/src/Imaging/ImageGenerator.php +++ b/src/Imaging/ImageGenerator.php @@ -3,6 +3,7 @@ namespace Statamic\Imaging; use Facades\Statamic\Imaging\ImageValidator; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\Filesystem; use League\Flysystem\UnableToReadFile; @@ -150,11 +151,17 @@ public function generateVideoThumbnail($asset, array $params) /** * Generate a manipulated image by an asset. * - * @param \Statamic\Contracts\Assets\Asset $asset + * @param \Statamic\Contracts\Assets\Asset|null $asset * @return mixed */ public function generateByAsset($asset, array $params) { + if (! $asset) { + Log::error('Cannot generate an image for a missing asset.'); + + return ''; + } + if ($asset->isVideo() && ThumbnailExtractor::available()) { return $this->generateVideoThumbnail($asset, $params); } diff --git a/src/Tags/Glide.php b/src/Tags/Glide.php index 8b3bcd861d8..705f9aeaecf 100644 --- a/src/Tags/Glide.php +++ b/src/Tags/Glide.php @@ -177,7 +177,18 @@ private function generateImage($item) : $this->getGenerator()->generateByPath($item, $params); } - return $this->getGenerator()->generateByAsset(Asset::find($item), $params); + $asset = Asset::find($item); + + if (! $asset) { + // Thrown (rather than logged here directly) so the calling closure's + // existing catch (\Exception $e) { Log::error($e->getMessage()); } + // in generate() logs it with the identifying $item, instead of the + // flat, context-free message generateByAsset()'s own null-asset + // guard would otherwise produce. + throw new \Exception('Cannot generate an image for a missing asset: '.(is_string($item) ? $item : json_encode($item))); + } + + return $this->getGenerator()->generateByAsset($asset, $params); } /** diff --git a/tests/Imaging/ImageGeneratorTest.php b/tests/Imaging/ImageGeneratorTest.php index 4fa2c833d96..1b13295db2b 100644 --- a/tests/Imaging/ImageGeneratorTest.php +++ b/tests/Imaging/ImageGeneratorTest.php @@ -98,6 +98,12 @@ public function it_generates_an_image_by_asset() Event::assertDispatchedTimes(GlideImageGenerated::class, 1); } + #[Test] + public function it_does_not_generate_an_image_for_a_missing_asset() + { + $this->assertSame('', $this->makeGenerator()->generateByAsset(null, ['w' => 100])); + } + #[Test] public function it_does_not_check_ffmpeg_availability_for_non_video_assets() { diff --git a/tests/Tags/GlideTest.php b/tests/Tags/GlideTest.php index 920aa24a253..1df330dce63 100644 --- a/tests/Tags/GlideTest.php +++ b/tests/Tags/GlideTest.php @@ -3,6 +3,7 @@ namespace Tests\Tags; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Facades\Log; use Orchestra\Testbench\Attributes\DefineEnvironment; use PHPUnit\Framework\Attributes\Test; use Statamic\Facades\File; @@ -11,6 +12,25 @@ class GlideTest extends TestCase { + #[Test] + /** + * https://github.com/statamic/cms/pull/15447 + */ + public function it_logs_the_item_when_the_asset_cannot_be_resolved() + { + Log::shouldReceive('error') + ->once() + ->with(\Mockery::pattern('/Cannot generate an image for a missing asset.*nonexistent\.jpg/')); + + $result = (string) Parse::template( + '{{ glide:foo width="100" }}', + ['foo' => 'nonexistent.jpg'], + trusted: true + ); + + $this->assertSame('', $result); + } + #[Test] #[DefineEnvironment('relativeRouteUrl')] public function it_outputs_a_relative_url_by_default_when_the_glide_route_is_relative()