2018-04-01
This commit is contained in:
parent
6c44946e39
commit
40600704f4
9 changed files with 933 additions and 7 deletions
9
app/Core/Exceptions/FileException.php
Normal file
9
app/Core/Exceptions/FileException.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Core\Exceptions;
|
||||
|
||||
use ForkBB\Core\Exceptions\ForkException;
|
||||
|
||||
class FileException extends ForkException
|
||||
{
|
||||
}
|
305
app/Core/File.php
Normal file
305
app/Core/File.php
Normal file
|
@ -0,0 +1,305 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Core;
|
||||
|
||||
use ForkBB\Core\Exceptions\FileException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class File
|
||||
{
|
||||
/**
|
||||
* Текст ошибки
|
||||
* @var null|string
|
||||
*/
|
||||
protected $error;
|
||||
|
||||
/**
|
||||
* Путь до файла
|
||||
* @var null|string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Содержимое файла
|
||||
* @var null|string
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Оригинальное имя файла без расширения
|
||||
* @var null|string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Оригинальное расширение файла
|
||||
* @var null|string
|
||||
*/
|
||||
protected $ext;
|
||||
|
||||
/**
|
||||
* Размер оригинального файла
|
||||
*/
|
||||
protected $size;
|
||||
|
||||
/**
|
||||
* Флаг автопереименования файла
|
||||
* @var bool
|
||||
*/
|
||||
protected $rename = false;
|
||||
|
||||
/**
|
||||
* Флаг перезаписи файла
|
||||
* @var bool
|
||||
*/
|
||||
protected $rewrite = false;
|
||||
|
||||
/**
|
||||
* Паттерн для pathinfo
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern = '%^(?!.*?\.\.)([\w.\x5C/:-]*[\x5C/])?(\*|[\w.-]+)\.(\*|[a-z\d]+)$%i';
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $options
|
||||
*
|
||||
* @throws FileException
|
||||
*/
|
||||
public function __construct($path, $options)
|
||||
{
|
||||
if (! \is_file($path)) {
|
||||
throw new FileException('File not found');
|
||||
}
|
||||
if (! \is_readable($path)) {
|
||||
throw new FileException('File can not be read');
|
||||
}
|
||||
|
||||
$this->path = $path;
|
||||
$this->data = null;
|
||||
|
||||
$name = null;
|
||||
$ext = null;
|
||||
if (isset($options['basename'])) {
|
||||
if (false === ($pos = \strrpos($options['basename'], '.'))) {
|
||||
$name = $options['basename'];
|
||||
} else {
|
||||
$name = \substr($options['basename'], 0, $pos);
|
||||
$ext = \substr($options['basename'], $pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
$this->name = isset($options['filename']) && \is_string($options['filename']) ? $options['filename'] : $name;
|
||||
$this->ext = isset($options['extension']) && \is_string($options['extension']) ? $options['extension'] : $ext;
|
||||
|
||||
$this->size = \is_string($this->data) ? \strlen($this->data) : \filesize($path);
|
||||
if (! $this->size) {
|
||||
throw new FileException('File size is undefined');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает текст ошибки
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function error()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Фильрует и переводит в латиницу(?) имя файла
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function filterName($name)
|
||||
{
|
||||
if (\function_exists('\transliterator_transliterate')) {
|
||||
$name = \transliterator_transliterate("Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove; Lower();", $name);
|
||||
}
|
||||
|
||||
$name = \trim(\preg_replace('%[^\w.-]+%', '-', $name), '-');
|
||||
|
||||
if (! isset($name{0})) {
|
||||
$name = (string) \time();
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает информацию о пути к сохраняемому файлу с учетом подстановок
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return false|array
|
||||
*/
|
||||
protected function pathinfo($path)
|
||||
{
|
||||
if (! \preg_match($this->pattern, $path, $matches)) {
|
||||
$this->error = 'The path/name format is broken';
|
||||
return false;
|
||||
}
|
||||
|
||||
if ('*' === $matches[2]) {
|
||||
$matches[2] = $this->filterName($this->name);
|
||||
}
|
||||
|
||||
if ('*' === $matches[3]) {
|
||||
$matches[3] = $this->ext;
|
||||
} elseif ('(' === $matches[3]{0} && ')' === $matches[3]{\strlen($matches[3]) - 1}) {
|
||||
$matches[3] = \explode('|', \substr($matches[3], 1, -1));
|
||||
|
||||
if (1 === \count($matches[3])) {
|
||||
$matches[3] = \array_pop($matches[3]);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'dirname' => $matches[1],
|
||||
'filename' => $matches[2],
|
||||
'extension' => $matches[3],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает флаг автопереименования файла
|
||||
*
|
||||
* @param bool $rename
|
||||
*
|
||||
* @return File
|
||||
*/
|
||||
public function rename($rename)
|
||||
{
|
||||
$this->rename = $rename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает флаг перезаписи файла
|
||||
*
|
||||
* @param bool $rewrite
|
||||
*
|
||||
* @return File
|
||||
*/
|
||||
public function rewrite($rewrite)
|
||||
{
|
||||
$this->rewrite = $rewrite;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает/проверяет на запись директорию
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function dirProc($dirname)
|
||||
{
|
||||
if (! \is_dir($dirname)) {
|
||||
if (! @\mkdir($dirname, 0755)) {
|
||||
$this->error = 'Can not create directory';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (! \is_writable($dirname)) {
|
||||
$this->error = 'No write access for directory';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает/устанавливает права на файл
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function fileProc($path)
|
||||
{
|
||||
if (\is_string($this->data)) {
|
||||
if (! \file_put_contents($this->path, $path)) {
|
||||
$this->error = 'Error writing file';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (! \copy($this->path, $path)) {
|
||||
$this->error = 'Error copying file';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@\chmod($path, 0644);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохраняет файл по указанному шаблону пути
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function toFile($path)
|
||||
{
|
||||
$info = $this->pathinfo($path);
|
||||
|
||||
if (false === $info || ! $this->dirProc($info['dirname'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->rename) {
|
||||
$old = $info['filename'];
|
||||
$i = 1;
|
||||
while (\file_exists($info['dirname'] . $info['filename'] . '.' . $info['extension'])) {
|
||||
++$i;
|
||||
$info['filename'] = $old . '_' . $i;
|
||||
}
|
||||
} elseif (! $this->rewrite && \file_exists($info['dirname'] . $info['filename'] . '.' . $info['extension'])) {
|
||||
$this->error = 'Such file already exists';
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = $info['dirname'] . $info['filename'] . '.' . $info['extension'];
|
||||
|
||||
if ($this->fileProc($path)) {
|
||||
$this->path = $path;
|
||||
$this->name = $info['filename'];
|
||||
$this->ext = $info['extension'];
|
||||
$this->size = \filesize($path);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function ext()
|
||||
{
|
||||
return $this->ext;
|
||||
}
|
||||
|
||||
public function size()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
326
app/Core/Files.php
Normal file
326
app/Core/Files.php
Normal file
|
@ -0,0 +1,326 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Core;
|
||||
|
||||
use ForkBB\Core\File;
|
||||
use ForkBB\Core\Image;
|
||||
use ForkBB\Core\Exceptions\FileException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Files
|
||||
{
|
||||
/**
|
||||
* Максимальный размер для картинок
|
||||
* @var int
|
||||
*/
|
||||
protected $maxImgSize;
|
||||
|
||||
/**
|
||||
* Максимальный размер для файлов
|
||||
* @var int
|
||||
*/
|
||||
protected $maxFileSize;
|
||||
|
||||
/**
|
||||
* Текст ошибки
|
||||
* @var null|string
|
||||
*/
|
||||
protected $error;
|
||||
|
||||
/**
|
||||
* Список кодов типов картинок и расширений для них
|
||||
* @var array
|
||||
*/
|
||||
protected $imageType = [
|
||||
1 => 'gif',
|
||||
2 => 'jpg',
|
||||
3 => 'png',
|
||||
4 => 'swf',
|
||||
5 => 'psd',
|
||||
6 => 'bmp',
|
||||
7 => 'tiff',
|
||||
8 => 'tiff',
|
||||
9 => 'jpc',
|
||||
10 => 'jp2',
|
||||
11 => 'jpx',
|
||||
12 => 'jb2',
|
||||
13 => 'swc',
|
||||
14 => 'iff',
|
||||
15 => 'wbmp',
|
||||
16 => 'xbm',
|
||||
17 => 'ico',
|
||||
18 => 'webp',
|
||||
];
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string|int $maxFileSize
|
||||
* @param string|int $maxImgSize
|
||||
*
|
||||
*/
|
||||
public function __construct($maxFileSize, $maxImgSize)
|
||||
{
|
||||
$init = \min(
|
||||
$this->size(\ini_get('upload_max_filesize')),
|
||||
$this->size(\ini_get('post_max_size'))
|
||||
);
|
||||
$this->maxImgSize = \min(
|
||||
$this->size($maxImgSize),
|
||||
$init
|
||||
);
|
||||
$this->maxFileSize = \min(
|
||||
$this->size($maxFileSize),
|
||||
$init
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает максимальный размер картинки для загрузки
|
||||
*
|
||||
* @param string $unit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function maxImgSize($unit = null)
|
||||
{
|
||||
return $this->size($this->maxImgSize, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает максимальный размер файла для загрузки
|
||||
*
|
||||
* @param string $unit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function maxFileSize($unit = null)
|
||||
{
|
||||
return $this->size($this->maxFileSize, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Переводит объем информации из одних единиц в другие
|
||||
*
|
||||
* @param int|string $value
|
||||
* @param string $to
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function size($value, $to = null)
|
||||
{
|
||||
if (\is_string($value)) {
|
||||
$value = \trim($value, "Bb \t\n\r\0\x0B");
|
||||
|
||||
if (! isset($value{0})) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$from = $value{\strlen($value) - 1};
|
||||
$value = (int) $value;
|
||||
|
||||
switch ($from) {
|
||||
case 'G':
|
||||
case 'g':
|
||||
$value *= 1024;
|
||||
case 'M':
|
||||
case 'm':
|
||||
$value *= 1024;
|
||||
case 'K':
|
||||
case 'k':
|
||||
$value *= 1024;
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_string($to)) {
|
||||
$to = \trim($to, "Bb \t\n\r\0\x0B");
|
||||
|
||||
switch ($to) {
|
||||
case 'G':
|
||||
case 'g':
|
||||
$value /= 1024;
|
||||
case 'M':
|
||||
case 'm':
|
||||
$value /= 1024;
|
||||
case 'K':
|
||||
case 'k':
|
||||
$value /= 1024;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает текст ошибки
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function error()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет по содержимому файла расширение картинки
|
||||
*
|
||||
* @param mixed $file
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function isImage($file)
|
||||
{
|
||||
if (\is_string($file)) {
|
||||
if (\function_exists('\exif_imagetype')) {
|
||||
$type = \exif_imagetype($file);
|
||||
} elseif (false !== ($type = @\getimagesize($file)) && $type[0] > 0 && $type[1] > 0) {
|
||||
$type = $type[2];
|
||||
} else {
|
||||
$type = 0;
|
||||
}
|
||||
return isset($this->imageType[$type]) ? $this->imageType[$type] : false;
|
||||
}
|
||||
|
||||
return $file instanceof Image ? $file->ext() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает файл(ы) из формы
|
||||
*
|
||||
* @param array $file
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function upload(array $file)
|
||||
{
|
||||
$this->error = null;
|
||||
|
||||
if (! isset($file['tmp_name'])
|
||||
|| ! isset($file['name'])
|
||||
|| ! isset($file['type'])
|
||||
|| ! isset($file['error'])
|
||||
|| ! isset($file['size'])
|
||||
) {
|
||||
$this->error = 'Expected file description array';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\is_array($file['tmp_name'])) {
|
||||
$result = [];
|
||||
foreach ($file['tmp_name'] as $key => $value) {
|
||||
// изображение не было отправлено
|
||||
if ('' === $file['name'][$key] && empty($file['size'][$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cur = $this->uploadOneFile([
|
||||
'tmp_name' => $value,
|
||||
'name' => $file['name'][$key],
|
||||
'type' => $file['type'][$key],
|
||||
'error' => $file['error'][$key],
|
||||
'size' => $file['size'][$key],
|
||||
]);
|
||||
|
||||
if (false === $cur) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result[] = $cur;
|
||||
}
|
||||
return empty($result) ? null : $result;
|
||||
} else {
|
||||
return '' === $file['name'] && empty($file['size']) ? null : $this->uploadOneFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает один файл из формы
|
||||
*
|
||||
* @param array $file
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function uploadOneFile(array $file)
|
||||
{
|
||||
if (\UPLOAD_ERR_OK !== $file['error']) {
|
||||
switch ($file['error']) {
|
||||
case \UPLOAD_ERR_INI_SIZE:
|
||||
$this->error = 'The uploaded file exceeds the upload_max_filesize';
|
||||
break;
|
||||
case \UPLOAD_ERR_FORM_SIZE:
|
||||
$this->error = 'The uploaded file exceeds the MAX_FILE_SIZE';
|
||||
break;
|
||||
case \UPLOAD_ERR_PARTIAL:
|
||||
$this->error = 'The uploaded file was only partially uploaded';
|
||||
break;
|
||||
case \UPLOAD_ERR_NO_FILE:
|
||||
$this->error = 'No file was uploaded';
|
||||
break;
|
||||
case \UPLOAD_ERR_NO_TMP_DIR:
|
||||
$this->error = 'Missing a temporary folder';
|
||||
break;
|
||||
case \UPLOAD_ERR_CANT_WRITE:
|
||||
$this->error = 'Failed to write file to disk';
|
||||
break;
|
||||
case \UPLOAD_ERR_EXTENSION:
|
||||
$this->error = 'A PHP extension stopped the file upload';
|
||||
break;
|
||||
default:
|
||||
$this->error = 'Unknown upload error';
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! \is_uploaded_file($file['tmp_name'])) {
|
||||
$this->error = 'The specified file was not uploaded';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === ($pos = \strrpos($file['name'], '.'))) {
|
||||
$name = $file['name'];
|
||||
$ext = null;
|
||||
} else {
|
||||
$name = \substr($file['name'], 0, $pos);
|
||||
$ext = \substr($file['name'], $pos + 1);
|
||||
}
|
||||
|
||||
$isImage = $this->isImage($file['tmp_name']);
|
||||
|
||||
if (false !== $isImage) {
|
||||
$ext = $isImage;
|
||||
$isImage = 'swf' !== $isImage; // флеш не будет картинкой
|
||||
}
|
||||
|
||||
if ($isImage) {
|
||||
if ($file['size'] > $this->maxImgSize) {
|
||||
$this->error = 'The image too large';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ($file['size'] > $this->maxFileSize) {
|
||||
$this->error = 'The file too large';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$options = [
|
||||
'filename' => $name,
|
||||
'extension' => $ext,
|
||||
'basename' => $name . '.' . $ext,
|
||||
'mime' => $file['type'],
|
||||
// 'size' => $file['size'],
|
||||
];
|
||||
|
||||
try {
|
||||
if ($isImage) {
|
||||
return new Image($file['tmp_name'], $options);
|
||||
} else {
|
||||
return new File($file['tmp_name'], $options);
|
||||
}
|
||||
} catch (FileException $e) {
|
||||
$this->error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
168
app/Core/Image.php
Normal file
168
app/Core/Image.php
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Core;
|
||||
|
||||
use ForkBB\Core\Files;
|
||||
use ForkBB\Core\File;
|
||||
use ForkBB\Core\Exceptions\FileException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Image extends File
|
||||
{
|
||||
/**
|
||||
* Изображение
|
||||
* @var false|resource
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* Качество изображения
|
||||
* @var int
|
||||
*/
|
||||
protected $quality = 100;
|
||||
|
||||
/**
|
||||
* Паттерн для pathinfo
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern = '%^(?!.*?\.\.)([\w.\x5C/:-]*[\x5C/])?(\*|[\w.-]+)\.(\*|[a-z\d]+|\([a-z\d]+(?:\|[a-z\d]+)*\))$%i';
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $options
|
||||
*
|
||||
* @throws FileException
|
||||
*/
|
||||
public function __construct($path, $options)
|
||||
{
|
||||
parent::__construct($path, $options);
|
||||
|
||||
if (! \extension_loaded('gd') || ! \function_exists('\imagecreatetruecolor')) {
|
||||
throw new FileException('GD library not connected');
|
||||
}
|
||||
|
||||
if (\is_string($this->data)) {
|
||||
$this->image = @\imagecreatefromstring($this->data);
|
||||
} else {
|
||||
$this->image = @\imagecreatefromstring(\file_get_contents($this->path));
|
||||
}
|
||||
|
||||
if (false === $this->image) {
|
||||
throw new FileException('Invalid image data');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Изменяет размер изображения при необходимости
|
||||
*
|
||||
* @param int $maxW
|
||||
* @param int $maxH
|
||||
*
|
||||
* @throws FileException
|
||||
*
|
||||
* @return Image
|
||||
*/
|
||||
public function resize($maxW, $maxH)
|
||||
{
|
||||
$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 = \round($oldW * $r);
|
||||
$height = \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 === \imagecopyresampled($image, $this->image, 0, 0, 0, 0, $width, $height, $oldW, $oldH)) {
|
||||
throw new FileException('Failed to resize image');
|
||||
}
|
||||
if (false === \imagealphablending($image, false) || false === \imagesavealpha($image, true)) {
|
||||
throw new FileException('Failed to adjust image');
|
||||
}
|
||||
|
||||
$this->image = $image;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает информацию о пути к сохраняемой картинке с учетом подстановок
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return false|array
|
||||
*/
|
||||
protected function pathinfo($path)
|
||||
{
|
||||
$info = parent::pathinfo($path);
|
||||
|
||||
if (false === $info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\is_array($info['extension'])) {
|
||||
if (\in_array($this->ext, $info['extension'])) {
|
||||
$info['extension'] = $this->ext;
|
||||
} else {
|
||||
$info['extension'] = \reset($info['extension']); // ???? выбор расширения?
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает/устанавливает права на картинку
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function fileProc($path)
|
||||
{
|
||||
switch (\pathinfo($path, \PATHINFO_EXTENSION)) {
|
||||
case 'jpg':
|
||||
$result = @\imagejpeg($this->image, $path, $this->quality);
|
||||
break;
|
||||
case 'png':
|
||||
$quality = \floor((100 - $this->quality) / 11);
|
||||
$result = @\imagepng($this->image, $path, $quality);
|
||||
break;
|
||||
case 'gif':
|
||||
$result = @\imagegif($this->image, $path);
|
||||
break;
|
||||
default:
|
||||
$this->error = 'File type not supported';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $result) {
|
||||
$this->error = 'Error writing file';
|
||||
return false;
|
||||
}
|
||||
@\chmod($path, 0644);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if (\is_resource($this->image)) {
|
||||
\imagedestroy($this->image);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace ForkBB\Core;
|
||||
|
||||
use ForkBB\Core\Container;
|
||||
use ForkBB\Core\File;
|
||||
use RuntimeException;
|
||||
|
||||
class Validator
|
||||
|
@ -108,6 +109,8 @@ class Validator
|
|||
'array' => [$this, 'vArray'],
|
||||
'checkbox' => [$this, 'vCheckbox'],
|
||||
'email' => [$this, 'vEmail'],
|
||||
'file' => [$this, 'vFile'],
|
||||
'image' => [$this, 'vImage'],
|
||||
'in' => [$this, 'vIn'],
|
||||
'integer' => [$this, 'vInteger'],
|
||||
'login' => [$this, 'vLogin'],
|
||||
|
@ -616,9 +619,22 @@ class Validator
|
|||
$this->addError('The :alias maximum is :attr');
|
||||
}
|
||||
} elseif (\is_array($value)) {
|
||||
if (\count($value) > $attr) {
|
||||
if (\reset($value) instanceof File) {
|
||||
$attr *= 1024;
|
||||
foreach ($value as $file) {
|
||||
if ($file->size() > $attr) {
|
||||
$this->addError('The :alias contains too large a file');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} elseif (\count($value) > $attr) {
|
||||
$this->addError('The :alias maximum is :attr elements');
|
||||
}
|
||||
} elseif ($value instanceof File) {
|
||||
if ($value->size() > $attr * 1024) {
|
||||
$this->addError('The :alias contains too large a file');
|
||||
return null;
|
||||
}
|
||||
} elseif (null !== $value) {
|
||||
$this->addError('The :alias maximum is :attr'); //????
|
||||
return null;
|
||||
|
@ -713,4 +729,49 @@ class Validator
|
|||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
protected function vFile($v, $value, $attr)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
if (! \is_array($value)) {
|
||||
$this->addError('The :alias not contains file');
|
||||
return null;
|
||||
}
|
||||
$value = $this->c->Files->upload($value);
|
||||
if (null === $value) {
|
||||
return null;
|
||||
} elseif (false === $value) {
|
||||
$this->addError($this->c->Files->error());
|
||||
return null;
|
||||
} elseif ('multiple' === $attr) {
|
||||
if (! \is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
} elseif (\is_array($value)) {
|
||||
$this->addError('The :alias contains more than one file');
|
||||
return null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function vImage($v, $value, $attr)
|
||||
{
|
||||
$value = $this->vFile($v, $value, $attr);
|
||||
|
||||
if (\is_array($value)) {
|
||||
foreach ($value as $file) {
|
||||
if (false === $this->c->Files->isImage($file)) {
|
||||
$this->addError('The :alias not contains image');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} elseif (null !== $value && false === $this->c->Files->isImage($value)) {
|
||||
$this->addError('The :alias not contains image');
|
||||
return null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace ForkBB\Models\Pages;
|
||||
|
||||
use ForkBB\Core\Image;
|
||||
use ForkBB\Models\Page;
|
||||
use ForkBB\Models\User\Model as User;
|
||||
|
||||
|
@ -52,16 +53,45 @@ class Profile extends Page
|
|||
$this->c->Lang->load('profile');
|
||||
|
||||
if ($isEdit && 'POST' === $method) {
|
||||
$v = $this->c->Validator->reset()
|
||||
->addValidators([
|
||||
])->addRules([
|
||||
'token' => 'token:EditUserProfile',
|
||||
'upload_avatar' => $rules->useAvatar ? 'image|max:' . $this->c->Files->maxImgSize('K') : 'absent',
|
||||
])->addAliases([
|
||||
])->addArguments([
|
||||
'token' => ['id' => $curUser->id],
|
||||
])->addMessages([
|
||||
]);
|
||||
|
||||
if ($v->validation($_FILES + $_POST)) {
|
||||
if ($v->upload_avatar instanceof Image) {
|
||||
$curUser->deleteAvatar();
|
||||
$v->upload_avatar
|
||||
->rename(false)
|
||||
->rewrite(true)
|
||||
->resize((int) $this->c->config->o_avatars_width, (int) $this->c->config->o_avatars_height)
|
||||
->toFile($this->c->DIR_PUBLIC . "{$this->c->config->o_avatars_dir}/{$curUser->id}.(jpg|png|gif)");
|
||||
# var_dump(
|
||||
# $v->upload_avatar->path(),
|
||||
# $v->upload_avatar->name(),
|
||||
# $v->upload_avatar->ext(),
|
||||
# $v->upload_avatar->size(),
|
||||
# $v->upload_avatar->error()
|
||||
# );
|
||||
}
|
||||
}
|
||||
|
||||
$this->fIswev = $v->getErrors();
|
||||
}
|
||||
|
||||
$clSuffix = $isEdit ? '-edit' : '';
|
||||
|
||||
if ($isEdit) {
|
||||
$form = [
|
||||
'action' => $this->c->Router->link('EditUserProfile', ['id' => $curUser->id]),
|
||||
'action' => $this->c->Router->link('EditUserProfile', ['id' => $curUser->id]),
|
||||
'hidden' => [
|
||||
'token' => $this->c->Csrf->create('EditUserProfile', ['id' => $curUser->id]),
|
||||
'token' => $this->c->Csrf->create('EditUserProfile', ['id' => $curUser->id]),
|
||||
],
|
||||
'sets' => [],
|
||||
'btns' => [
|
||||
|
@ -141,7 +171,7 @@ class Profile extends Page
|
|||
}
|
||||
if ($isEdit) {
|
||||
$form['enctype'] = 'multipart/form-data';
|
||||
$form['hidden']['MAX_FILE_SIZE'] = 999999999;
|
||||
$form['hidden']['MAX_FILE_SIZE'] = $this->c->Files->maxImgSize();
|
||||
|
||||
$fields['upload_avatar'] = [
|
||||
'id' => 'upload_avatar',
|
||||
|
|
|
@ -9,6 +9,12 @@ use RuntimeException;
|
|||
|
||||
class Model extends DataModel
|
||||
{
|
||||
/**
|
||||
* Типы аватарок
|
||||
* @var array
|
||||
*/
|
||||
protected $avatarTypes = ['jpg', 'gif', 'png'];
|
||||
|
||||
/**
|
||||
* Статус неподтвержденного
|
||||
*
|
||||
|
@ -157,9 +163,7 @@ class Model extends DataModel
|
|||
*/
|
||||
protected function getavatar()
|
||||
{
|
||||
$filetypes = ['jpg', 'gif', 'png'];
|
||||
|
||||
foreach ($filetypes as $type) {
|
||||
foreach ($this->avatarTypes as $type) {
|
||||
$path = $this->c->DIR_PUBLIC . "{$this->c->config->o_avatars_dir}/{$this->id}.{$type}";
|
||||
|
||||
if (\is_file($path) && \getimagesize($path)) {
|
||||
|
@ -170,6 +174,20 @@ class Model extends DataModel
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет аватару пользователя
|
||||
*/
|
||||
public function deleteAvatar()
|
||||
{
|
||||
foreach ($this->avatarTypes as $type) {
|
||||
$path = $this->c->DIR_PUBLIC . "{$this->c->config->o_avatars_dir}/{$this->id}.{$type}";
|
||||
|
||||
if (\is_file($path)) {
|
||||
@\unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Титул пользователя
|
||||
*
|
||||
|
|
|
@ -35,6 +35,8 @@ return [
|
|||
'smTplBl' => ['url'],
|
||||
],
|
||||
'MAX_POST_SIZE' => 65536,
|
||||
'MAX_IMG_SIZE' => '2M',
|
||||
'MAX_FILE_SIZE' => '2M',
|
||||
|
||||
'shared' => [
|
||||
'DB' => [
|
||||
|
@ -110,6 +112,11 @@ return [
|
|||
'class' => \ForkBB\Core\Parser::class,
|
||||
'flag' => ENT_HTML5,
|
||||
],
|
||||
'Files' => [
|
||||
'class' => \ForkBB\Core\Files::class,
|
||||
'file' => '%MAX_FILE_SIZE%',
|
||||
'img' => '%MAX_IMG_SIZE%',
|
||||
],
|
||||
|
||||
],
|
||||
'multiple' => [
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
},
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"ext-gd": "*",
|
||||
"ext-mbstring": "*",
|
||||
"artoodetoo/dirk": "dev-master",
|
||||
"miovisman/parserus": "dev-master"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue