Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 4 additions & 69 deletions src/Image/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -378,76 +378,11 @@ public function save(?string $path = null, string $type = '', int $quality = 75)
break;

case 'webp':
$temp = null;
$output = null;
try {
if ($quality >= 0) {
$this->image->setImageCompressionQuality($quality);
}
$this->image->setImageFormat('webp');

if (empty($path)) {
return $this->image->getImagesBlob();
} else {
$this->image->writeImages($path, true);
}
} catch (\Throwable) {
$signature = $this->image->getImageSignature();

$temp = tempnam(sys_get_temp_dir(), 'temp-'.$signature);
if ($temp === false) {
throw new Exception('Failed to create temporary file');
}

$output = tempnam(sys_get_temp_dir(), 'output-'.$signature);
if ($output === false) {
\unlink($temp);
throw new Exception('Failed to create output file');
}

$temp .= '.'.\strtolower($this->image->getImageFormat());
$output .= '.'.$type;

// save temp
$this->image->writeImages($temp, true);

// convert temp
$quality = (int) $quality;
$command = \sprintf(
'cwebp -quiet -metadata none -q %d %s -o %s',
$quality,
\escapeshellarg($temp),
\escapeshellarg($output)
);
\exec($command, $outputArray, $returnCode);

if ($returnCode !== 0) {
throw new Exception('Image conversion failed');
}

$data = \file_get_contents($output);

// save to path
if (! empty($path)) {
\file_put_contents($path, $data, LOCK_EX);

return;
}

return $data;
} finally {
if (is_string($temp) && file_exists($temp)) {
\unlink($temp);
}
if (is_string($output) && file_exists($output)) {
\unlink($output);
}

$this->image->clear();
$this->image->destroy();
if ($quality >= 0) {
$this->image->setImageCompressionQuality($quality);
}

return;
$this->image->setImageFormat('webp');
break;

case 'png':
if ($quality >= 0) {
Expand Down
40 changes: 40 additions & 0 deletions tests/Image/ImageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,46 @@ public function test_crop100x100_webp_quality30(): void
\unlink($target);
}

public function test_webp_blob_output(): void
{
$image = new Image(\file_get_contents(__DIR__.'/../resources/disk-a/kitten-1.jpg') ?: '');

$image->crop(100, 100);

$blob = $image->output('webp', 75);

$this->assertIsString($blob);
$this->assertNotEmpty($blob);
$this->assertSame('RIFF', \substr($blob, 0, 4));
$this->assertSame('WEBP', \substr($blob, 8, 4));

$probe = new \Imagick;
$probe->readImageBlob($blob);
$this->assertEquals(100, $probe->getImageWidth());
$this->assertEquals(100, $probe->getImageHeight());
$this->assertTrue(in_array($probe->getImageFormat(), ['PAM', 'WEBP']));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Overly permissive format assertion

'PAM' (Netpbm Portable Arbitrary Map) is accepted alongside 'WEBP' as a valid output format. In test_webp_blob_output this is harmless since the RIFF/WEBP magic-byte assertions on lines 425–426 already confirm the bytes are real WebP. But the same loose assertion is repeated in test_webp_from_webp_input (line 450) where there is no magic-byte guard, so a file saved in PAM format would silently pass the whole test. If PAM is a known quirk of a specific Imagick build, a brief comment would clarify the intent; otherwise tightening the assertion to 'WEBP' alone would give stronger coverage.

}

public function test_webp_from_webp_input(): void
{
$image = new Image(\file_get_contents(__DIR__.'/../resources/resize/100x100.webp') ?: '');
$target = __DIR__.'/roundtrip.webp';

$image->crop(50, 50);

$image->save($target, 'webp', 75);

$this->assertFileExists($target);
$this->assertNotEmpty(\file_get_contents($target));

$probe = new \Imagick($target);
$this->assertEquals(50, $probe->getImageWidth());
$this->assertEquals(50, $probe->getImageHeight());
$this->assertTrue(in_array($probe->getImageFormat(), ['PAM', 'WEBP']));

\unlink($target);
}

public function test_crop100x100_avif(): void
{
$image = new Image(\file_get_contents(filename: __DIR__.'/../resources/disk-a/kitten-1.jpg') ?: '');
Expand Down
Loading