mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Reinstated GDBackend->checkAvailableMemory()
This was removed as part of the SS4 assets refactor:
be239896d3
Presumably @tractorcow didn’t feel it’s possible to retain this,
because we don’t have local file handles required for getimagesize().
Since there’s getimagesizefromstring() as well (added in PHP 5.4),
we just needed a different logic branch.
Also makes the logic more resilient against missing GD resources on a backend.
Lack of available memory for a resize is only one (new) reason,
other edge cases were already causing these missing resources (e.g. an invalid file string).
Original discussion: https://groups.google.com/forum/m/#!topic/silverstripe-dev/B57a3KYeAVQ
Pull request for 3.x: https://github.com/silverstripe/silverstripe-framework/pull/2859
More context: https://github.com/silverstripe/silverstripe-framework/pull/2569
This commit is contained in:
parent
6e561f00bd
commit
fcb511b1c0
@ -99,7 +99,7 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
|
||||
// We use getimagesize instead of extension checking, because sometimes extensions are wrong.
|
||||
$meta = getimagesize($path);
|
||||
if ($meta === false) {
|
||||
if ($meta === false || !$this->checkAvailableMemory($meta)) {
|
||||
$this->markFailed($path, $mtime);
|
||||
return;
|
||||
}
|
||||
@ -147,7 +147,7 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if failed before
|
||||
// Skip if failed before, or image is too large
|
||||
$filename = $assetContainer->getFilename();
|
||||
$hash = $assetContainer->getHash();
|
||||
$variant = $assetContainer->getVariant();
|
||||
@ -155,8 +155,16 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as potentially failed prior to creation, resetting this on success
|
||||
$content = $assetContainer->getString();
|
||||
|
||||
// We use getimagesizefromstring instead of extension checking, because sometimes extensions are wrong.
|
||||
$meta = getimagesizefromstring($content);
|
||||
if ($meta === false || !$this->checkAvailableMemory($meta)) {
|
||||
$this->markFailed($filename, $hash, $variant);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as potentially failed prior to creation, resetting this on success
|
||||
$image = imagecreatefromstring($content);
|
||||
if ($image === false) {
|
||||
$this->markFailed($filename, $hash, $variant);
|
||||
@ -214,6 +222,35 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
return (bool)$this->cache->load($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we've got enough memory available for resampling this image. This check is rough,
|
||||
* so it will not catch all images that are too large - it also won't work accurately on large,
|
||||
* animated GIFs as bits per pixel can't be calculated for an animated GIF with a global color
|
||||
* table.
|
||||
*
|
||||
* @param array $imageInfo Value from getimagesize() or getimagesizefromstring()
|
||||
* @return boolean
|
||||
*/
|
||||
protected function checkAvailableMemory($imageInfo)
|
||||
{
|
||||
$limit = translate_memstring(ini_get('memory_limit'));
|
||||
if ($limit < 0) {
|
||||
return true; // memory_limit == -1
|
||||
}
|
||||
|
||||
// bits per channel (rounded up, default to 1)
|
||||
$bits = isset($imageInfo['bits']) ? ($imageInfo['bits'] + 7) / 8 : 1;
|
||||
|
||||
// channels (default 4 rgba)
|
||||
$channels = isset($imageInfo['channels']) ? $imageInfo['channels'] : 4;
|
||||
$bytesPerPixel = $bits * $channels;
|
||||
|
||||
// width * height * bytes per pixel
|
||||
$memoryRequired = $imageInfo[0] * $imageInfo[1] * $bytesPerPixel;
|
||||
|
||||
return $memoryRequired + memory_get_usage() < $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a file as failed
|
||||
*
|
||||
@ -622,16 +659,31 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
if ($extension = pathinfo($filename, PATHINFO_EXTENSION)) {
|
||||
$path .= "." . $extension;
|
||||
}
|
||||
$this->writeTo($path);
|
||||
|
||||
$writeSuccess = $this->writeTo($path);
|
||||
if (!$writeSuccess) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $assetStore->setFromLocalFile($path, $filename, $hash, $variant, $config);
|
||||
unlink($path);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return boolean
|
||||
*/
|
||||
public function writeTo($filename)
|
||||
{
|
||||
if (!$filename) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The GD resource might not exist if the image is too large to be processed, see checkAvailableMemory().
|
||||
if (!$this->gd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current image data
|
||||
@ -660,7 +712,7 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
}
|
||||
}
|
||||
|
||||
// if $this->interlace != 0, the output image will be interlaced
|
||||
// If $this->interlace != 0, the output image will be interlaced.
|
||||
imageinterlace($this->gd, $this->interlace);
|
||||
|
||||
// if the extension does not exist, the file will not be created!
|
||||
@ -679,9 +731,14 @@ class GDBackend extends Object implements Image_Backend, Flushable
|
||||
imagepng($this->gd, $filename);
|
||||
break;
|
||||
}
|
||||
if (file_exists($filename)) {
|
||||
@chmod($filename, 0664);
|
||||
|
||||
if (!file_exists($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@chmod($filename, 0664);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user