Working with images moved from Core\Image to separate drivers

This commit is contained in:
Visman 2021-11-27 20:21:44 +07:00
parent 94002ba6cc
commit 21b2b03ff1
8 changed files with 304 additions and 82 deletions

View file

@ -12,8 +12,10 @@ namespace ForkBB\Core;
use ForkBB\Core\File;
use ForkBB\Core\Image;
use ForkBB\Core\Image\DefaultDriver;
use ForkBB\Core\Exceptions\FileException;
use InvalidArgumentException;
use RuntimeException;
class Files
{
@ -35,6 +37,12 @@ class Files
*/
protected $error;
/**
* Класс обработки изображений
* @var DefaultDriver
*/
protected $imgDriver;
/**
* Список mime типов считающихся картинками
* @var array
@ -834,7 +842,7 @@ class Files
'image/avif' => 'avif',
];
public function __construct(/* string|int */ $maxFileSize, /* string|int */ $maxImgSize)
public function __construct(/* string|int */ $maxFileSize, /* string|int */ $maxImgSize, array $imgDrivers)
{
$init = \min(
\PHP_INT_MAX,
@ -849,6 +857,23 @@ class Files
$this->size($maxFileSize),
$init
);
$this->imgDriver = $this->imgDriver($imgDrivers);
}
/**
* Возращает драйвер для работы с изображениями
*/
protected function imgDriver(array $arr): DefaultDriver
{
foreach ($arr as $class) {
$driver = new $class($this);
if (true === $driver->ready()) {
return $driver;
}
}
throw new RuntimeException('No driver for work with images');
}
/**
@ -1107,7 +1132,7 @@ class Files
try {
if (null !== $imageExt) {
return new Image($file['tmp_name'], $options);
return new Image($file['tmp_name'], $options, $this->imgDriver);
} else {
return new File($file['tmp_name'], $options);
}

View file

@ -12,6 +12,7 @@ namespace ForkBB\Core;
use ForkBB\Core\Files;
use ForkBB\Core\File;
use ForkBB\Core\Image\DefaultDriver;
use ForkBB\Core\Exceptions\FileException;
use InvalidArgumentException;
@ -19,10 +20,16 @@ class Image extends File
{
/**
* Изображение
* @var false|resource
* @var mixed
*/
protected $image;
/**
* Класс обработки изображений
* @var DefaultDriver
*/
protected $imgDriver;
/**
* Качество изображения
* @var int
@ -35,18 +42,20 @@ class Image extends File
*/
protected $pattern = '%^(?!.*?\.\.)([\w.\x5C/:-]*[\x5C/])?(\*|[\w.-]+)\.(\*|[a-z\d]+|\([a-z\d]+(?:\|[a-z\d]+)*\))$%i';
public function __construct(string $path, array $options)
public function __construct(string $path, array $options, DefaultDriver $imgDriver)
{
parent::__construct($path, $options);
if (! \extension_loaded('gd')) {
throw new FileException('GD library not enabled');
if ($imgDriver::DEFAULT) {
throw new FileException('No library for work with images');
}
$this->imgDriver = $imgDriver;
if (\is_string($this->data)) {
$this->image = \imagecreatefromstring($this->data);
$this->image = $imgDriver->readFromStr($this->data);
} else {
$this->image = \imagecreatefromstring(\file_get_contents($this->path));
$this->image = $imgDriver->readFromPath($this->path);
}
if (false === $this->image) {
@ -59,44 +68,7 @@ class Image extends File
*/
public function resize(int $maxW, int $maxH): Image
{
$oldW = \imagesx($this->image);
$oldH = \imagesy($this->image);
$wr = ($maxW < 1) ? 1 : $maxW / $oldW;
$hr = ($maxH < 1) ? 1 : $maxH / $oldH;
$r = \min($wr, $hr, 1);
$width = (int) \round($oldW * $r);
$height = (int) \round($oldH * $r);
if (false === ($image = \imagecreatetruecolor($width, $height))) {
throw new FileException('Failed to create new truecolor image');
}
if (false === ($color = \imagecolorallocatealpha($image, 255, 255, 255, 127))) {
throw new FileException('Failed to create color for image');
}
if (false === \imagefill($image, 0, 0, $color)) {
throw new FileException('Failed to fill image with color');
}
\imagecolortransparent($image, $color);
$palette = \imagecolorstotal($this->image);
if (
$palette > 0
&& ! \imagetruecolortopalette($image, true, $palette)
) {
throw new FileException('Failed to convert image to palette');
}
if (
false === \imagealphablending($image, false)
|| false === \imagesavealpha($image, true)
) {
throw new FileException('Failed to adjust image');
}
if (false === \imagecopyresampled($image, $this->image, 0, 0, 0, 0, $width, $height, $oldW, $oldH)) {
throw new FileException('Failed to resize image');
}
$this->image = $image;
$this->image = $this->imgDriver->resize($this->image, $maxW, $maxH);
return $this;
}
@ -128,43 +100,22 @@ class Image extends File
*/
protected function fileProc(string $path): bool
{
switch (\pathinfo($path, \PATHINFO_EXTENSION)) {
case 'jpg':
$result = \imagejpeg($this->image, $path, $this->quality);
break;
case 'png':
$quality = (int) \floor((100 - $this->quality) / 11);
$result = \imagepng($this->image, $path, $quality);
break;
case 'gif':
$result = \imagegif($this->image, $path);
break;
case 'webp':
$result = \imagewebp($this->image, $path, $this->quality);
break;
case 'avif':
$result = \imageavif($this->image, $path, $this->quality);
break;
default:
$this->error = 'File type not supported';
$result = $this->imgDriver->writeToPath($this->image, $path, $this->quality);
return false;
}
if (! $result) {
if (null === $result) {
$result = false;
$this->error = 'File type not supported';
} elseif (! $result) {
$this->error = 'Error writing file';
return false;
} else {
\chmod($path, 0644);
}
\chmod($path, 0644);
return true;
return $result;
}
public function __destruct() {
if (\is_resource($this->image)) {
\imagedestroy($this->image);
}
public function __destruct()
{
$this->imgDriver->destroy($this->image);
}
}

View file

@ -0,0 +1,64 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\Image;
use ForkBB\Core\Files;
use ForkBB\Core\Exceptions\FileException;
class DefaultDriver
{
const DEFAULT = true;
/**
* @var bool
*/
protected $ready;
/**
* @var Files
*/
protected $files;
public function __construct(Files $files)
{
$this->ready = true;
$this->files = $files;
}
public function ready(): bool
{
return $this->ready;
}
public function readFromStr(string $data) /* : mixed|false */
{
return false;
}
public function readFromPath(string $path) /* : mixed|false */
{
return false;
}
public function writeToPath(/* mixed */ $image, string $path, int $quality): ?bool
{
return null;
}
public function resize(/* mixed */ $image, int $maxW, int $maxH) /* : mixed */
{
return $image;
}
public function destroy(/* mixed */ $image): void
{
}
}

127
app/Core/Image/GDDriver.php Normal file
View file

@ -0,0 +1,127 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\Image;
use ForkBB\Core\Files;
use ForkBB\Core\Image\DefaultDriver;
use ForkBB\Core\Exceptions\FileException;
class GDDriver extends DefaultDriver
{
const DEFAULT = false;
public function __construct(Files $files)
{
parent::__construct($files);
$this->ready = \extension_loaded('gd') && \function_exists('\\imagecreatetruecolor');
}
public function readFromStr(string $data) /* : mixed|false */
{
return $this->ready ? \imagecreatefromstring($data) : false;
}
public function readFromPath(string $path) /* : mixed|false */
{
if (
! $this->ready
|| $this->files->isBadPath($path)
) {
return false;
} else {
return \imagecreatefromstring(\file_get_contents($path));
}
}
public function writeToPath(/* mixed */ $image, string $path, int $quality): ?bool
{
$args = [$image, $path];
$type = \pathinfo($path, \PATHINFO_EXTENSION);
switch ($type) {
case 'gif':
break;
case 'png':
$args[] = (int) \floor((100 - $quality) / 11);
break;
case 'jpg':
$type = 'jpeg';
case 'webp':
case 'avif':
$args[] = $quality;
break;
default:
return null;
}
$function = '\\image' . $type;
if (\function_exists($function)) {
return $function(...$args);
} else {
return null;
}
}
public function resize(/* mixed */ $image, int $maxW, int $maxH) /* : mixed */
{
if (! $this->ready) {
throw new FileException('GD library not enabled');
}
$oldW = \imagesx($image);
$oldH = \imagesy($image);
$wr = ($maxW < 1) ? 1 : $maxW / $oldW;
$hr = ($maxH < 1) ? 1 : $maxH / $oldH;
$r = \min($wr, $hr, 1);
$width = (int) \round($oldW * $r);
$height = (int) \round($oldH * $r);
if (false === ($result = \imagecreatetruecolor($width, $height))) {
throw new FileException('Failed to create new truecolor image');
}
if (false === ($color = \imagecolorallocatealpha($result, 255, 255, 255, 127))) {
throw new FileException('Failed to create color for image');
}
if (false === \imagefill($result, 0, 0, $color)) {
throw new FileException('Failed to fill image with color');
}
\imagecolortransparent($result, $color);
$palette = \imagecolorstotal($image);
if (
$palette > 0
&& ! \imagetruecolortopalette($result, true, $palette)
) {
throw new FileException('Failed to convert image to palette');
}
if (
false === \imagealphablending($result, false)
|| false === \imagesavealpha($result, true)
) {
throw new FileException('Failed to adjust image');
}
if (false === \imagecopyresampled($result, $image, 0, 0, 0, 0, $width, $height, $oldW, $oldH)) {
throw new FileException('Failed to resize image');
}
return $result;
}
public function destroy(/* mixed */ $image): void
{
if (\is_resource($image)) {
\imagedestroy($image);
}
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\Image;
use ForkBB\Core\Files;
use ForkBB\Core\Image\DefaultDriver;
use ForkBB\Core\Exceptions\FileException;
class ImagickDriver extends DefaultDriver
{
const DEFAULT = false;
public function __construct(Files $files)
{
parent::__construct($files);
$this->ready = \extension_loaded('imagick') && \class_exists('\\Imagick');
}
}

View file

@ -1593,4 +1593,25 @@ class Update extends Admin
return null;
}
/**
* rev.38 to rev.39
*/
protected function stageNumber38(array $args): ?int
{
$coreConfig = new CoreConfig($this->configFile);
$coreConfig->add(
'shared=>Files=>drivers',
[
'\\ForkBB\\Core\\Image\\ImagickDriver::class',
'\\ForkBB\\Core\\Image\\GDDriver::class',
'\\ForkBB\\Core\\Image\\DefaultDriver::class',
]
);
$coreConfig->save();
return null;
}
}

View file

@ -52,7 +52,7 @@ if (
}
$c->PUBLIC_URL = $c->BASE_URL . $forkPublicPrefix;
$c->FORK_REVISION = 38;
$c->FORK_REVISION = 39;
$c->START = $forkStart;
$c->DIR_APP = __DIR__;
$c->DIR_PUBLIC = \realpath(__DIR__ . '/../public');

View file

@ -173,9 +173,14 @@ return [
'flag' => \ENT_HTML5,
],
'Files' => [
'class' => \ForkBB\Core\Files::class,
'file' => '%MAX_FILE_SIZE%',
'img' => '%MAX_IMG_SIZE%',
'class' => \ForkBB\Core\Files::class,
'file' => '%MAX_FILE_SIZE%',
'img' => '%MAX_IMG_SIZE%',
'drivers' => [
\ForkBB\Core\Image\ImagickDriver::class,
\ForkBB\Core\Image\GDDriver::class,
\ForkBB\Core\Image\DefaultDriver::class,
],
],
'VLnoURL' => \ForkBB\Models\Validators\NoURL::class,