forkbb/app/Core/File.php
2020-09-12 23:22:32 +07:00

286 lines
7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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';
public function __construct(string $path, array $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');
}
}
/**
* Возвращает текст ошибки
*/
public function error(): ?string
{
return $this->error;
}
/**
* Фильрует и переводит в латиницу(?) имя файла
*/
protected function filterName(string $name): string
{
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;
}
/**
* Возвращает информацию о пути к сохраняемому файлу с учетом подстановок
*/
protected function pathinfo(string $path): ?array
{
if (! \preg_match($this->pattern, $path, $matches)) {
$this->error = 'The path/name format is broken';
return null;
}
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],
];
}
/**
* Устанавливает флаг автопереименования файла
*/
public function rename(bool $rename): File
{
$this->rename = $rename;
return $this;
}
/**
* Устанавливает флаг перезаписи файла
*/
public function rewrite(bool $rewrite): File
{
$this->rewrite = $rewrite;
return $this;
}
/**
* Создает/проверяет на запись директорию
*/
protected function dirProc(string $dirname): bool
{
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;
}
/**
* Создает/устанавливает права на файл
*/
protected function fileProc(string $path): bool
{
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;
}
/**
* Сохраняет файл по указанному шаблону пути
*/
public function toFile(string $path): bool
{
$info = $this->pathinfo($path);
if (
null === $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(): string
{
return $this->name;
}
public function ext(): string
{
return $this->ext;
}
public function size(): int
{
return $this->size;
}
public function path(): string
{
return $this->path;
}
}