Working with images moved from Core\Image to separate drivers
This commit is contained in:
parent
94002ba6cc
commit
21b2b03ff1
8 changed files with 304 additions and 82 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
64
app/Core/Image/DefaultDriver.php
Normal file
64
app/Core/Image/DefaultDriver.php
Normal 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
127
app/Core/Image/GDDriver.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
29
app/Core/Image/ImagickDriver.php
Normal file
29
app/Core/Image/ImagickDriver.php
Normal 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');
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue