Refactor code with Rector

This commit is contained in:
Giuseppe Criscione 2023-12-28 12:49:13 +01:00
parent 0f87c1b375
commit a992d69d98
128 changed files with 762 additions and 1131 deletions

View File

@ -6,14 +6,18 @@ use Formwork\Languages\Languages;
use Formwork\Utils\Constraint;
use Formwork\Utils\Date;
use Formwork\Utils\Str;
use InvalidArgumentException;
return function (Languages $languages) {
return [
'format' => function (Field $field, ?string $format = null, string $type = 'pattern'): string {
$format = match (strtolower($type)) {
'pattern' => Date::patternToFormat($format),
'date' => $format
};
if ($format !== null) {
$format = match (strtolower($type)) {
'pattern' => Date::patternToFormat($format),
'date' => $format,
default => throw new InvalidArgumentException('Invalid date format type')
};
}
return $field->isEmpty() ? '' : Date::formatTimestamp($field->toTimestamp(), $format);
},

View File

@ -7,7 +7,7 @@ use Formwork\Fields\Field;
return function (App $app) {
return [
'validate' => function (Field $field, $value): int {
'validate' => function (Field $field, $value): int|float {
if (!is_numeric($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

View File

@ -33,7 +33,7 @@ return function (App $app) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not match the required pattern', $field->name(), $field->value()));
}
return (string) $value;
return $value;
},
];

View File

@ -11,7 +11,7 @@ return function (Site $site) {
return [
'toHTML' => function (Field $field) use ($site): string {
$currentPage = $site->currentPage();
return Markdown::parse((string) $field->value(), ['baseRoute' => $currentPage ? $currentPage->route() : '/']);
return Markdown::parse((string) $field->value(), ['baseRoute' => $currentPage !== null ? $currentPage->route() : '/']);
},
'toString' => function (Field $field): string {
@ -35,17 +35,15 @@ return function (Site $site) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
if ($field->has('min') && strlen($value) < $field->get('min')) {
if ($field->has('min') && strlen((string) $value) < $field->get('min')) {
throw new ValidationException(sprintf('The minimum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('min')));
}
if ($field->has('max') && strlen($value) > $field->get('max')) {
if ($field->has('max') && strlen((string) $value) > $field->get('max')) {
throw new ValidationException(sprintf('The maximum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('max')));
}
$value = str_replace("\r\n", "\n", $value);
return $value;
return str_replace("\r\n", "\n", (string) $value);
},
];

View File

@ -8,7 +8,7 @@ use Formwork\Utils\Constraint;
return function (App $app) {
return [
'validate' => function (Field $field, $value): ?string {
'validate' => function (Field $field, $value): string {
if (Constraint::isEmpty($value)) {
return '';
}
@ -17,15 +17,15 @@ return function (App $app) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
if ($field->has('min') && strlen($value) < $field->get('min')) {
if ($field->has('min') && strlen((string) $value) < $field->get('min')) {
throw new ValidationException(sprintf('The minimum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('min')));
}
if ($field->has('max') && strlen($value) > $field->get('max')) {
if ($field->has('max') && strlen((string) $value) > $field->get('max')) {
throw new ValidationException(sprintf('The maximum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('max')));
}
if ($field->has('pattern') && !Constraint::matchesRegex($value, $field->get('pattern'))) {
if ($field->has('pattern') && !Constraint::matchesRegex((string) $value, $field->get('pattern'))) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not match the required pattern', $field->name(), $field->value()));
}

View File

@ -7,7 +7,7 @@ use Formwork\Fields\Field;
return function (App $app) {
return [
'validate' => function (Field $field, $value): int {
'validate' => function (Field $field, $value): int|float {
if (!is_numeric($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

View File

@ -17,15 +17,15 @@ return function (App $app) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
if ($field->has('min') && strlen($value) < $field->get('min')) {
if ($field->has('min') && strlen((string) $value) < $field->get('min')) {
throw new ValidationException(sprintf('The minimum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('min')));
}
if ($field->has('max') && strlen($value) > $field->get('max')) {
if ($field->has('max') && strlen((string) $value) > $field->get('max')) {
throw new ValidationException(sprintf('The maximum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('max')));
}
if ($field->has('pattern') && !Constraint::matchesRegex($value, $field->get('pattern'))) {
if ($field->has('pattern') && !Constraint::matchesRegex((string) $value, $field->get('pattern'))) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not match the required pattern', $field->name(), $field->value()));
}

View File

@ -16,17 +16,15 @@ return function (App $app) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
if ($field->has('min') && strlen($value) < $field->get('min')) {
if ($field->has('min') && strlen((string) $value) < $field->get('min')) {
throw new ValidationException(sprintf('The minimum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('min')));
}
if ($field->has('max') && strlen($value) > $field->get('max')) {
if ($field->has('max') && strlen((string) $value) > $field->get('max')) {
throw new ValidationException(sprintf('The maximum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('max')));
}
$value = str_replace("\r\n", "\n", (string) $value);
return $value;
return str_replace("\r\n", "\n", (string) $value);
},
];

View File

@ -34,7 +34,7 @@ return function (App $app) {
$currentPage = $app->site()->currentPage();
return Markdown::parse(
$markdown,
['baseRoute' => $currentPage ? $currentPage->route() : '/']
['baseRoute' => $currentPage !== null ? $currentPage->route() : '/']
);
},

View File

@ -34,4 +34,4 @@ $_SERVER['SCRIPT_FILENAME'] = $root . DIRECTORY_SEPARATOR . 'index.php';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['PHP_SELF'] = '/index.php';
require 'index.php';
require __DIR__ . '/index.php';

View File

@ -64,7 +64,7 @@ final class App
if ($this->container->has($name)) {
return $this->container->get($name);
}
throw new BadMethodCallException(sprintf('Call to undefined method %s::%s()', static::class, $name));
throw new BadMethodCallException(sprintf('Call to undefined method %s::%s()', self::class, $name));
}
public function config(): Config
@ -141,7 +141,7 @@ final class App
{
$container->define(Container::class, $container);
$container->define(static::class, $this);
$container->define(self::class, $this);
$container->define(Config::class)
->loader(ConfigServiceLoader::class)

View File

@ -51,15 +51,15 @@ class Backupper
$destination = FileSystem::joinPaths($path, $name);
$zip = new ZipArchive();
$zipArchive = new ZipArchive();
if (($status = $zip->open($destination, ZipArchive::CREATE)) === true) {
if (($status = $zipArchive->open($destination, ZipArchive::CREATE)) === true) {
foreach (FileSystem::listRecursive($source, FileSystem::LIST_ALL) as $file) {
if ($this->isCopiable($file)) {
$zip->addFile($file, $file);
$zipArchive->addFile($file, $file);
}
}
$zip->close();
$zipArchive->close();
}
$this->deleteOldBackups();

View File

@ -8,22 +8,11 @@ use Formwork\Utils\FileSystem;
class FilesCache extends AbstractCache
{
/**
* Cache path
* @param string $path Cache path
* @param int $defaultTtl Cached data time-to-live
*/
protected string $path;
/**
* Cached data time-to-live
*/
protected int $defaultTtl;
/**
* Create a new FilesCache instance
*/
public function __construct(string $path, int $defaultTtl)
public function __construct(protected string $path, protected int $defaultTtl)
{
$this->path = $path;
$this->defaultTtl = $defaultTtl;
if (!FileSystem::exists($this->path)) {
FileSystem::createDirectory($this->path, recursive: true);
}

View File

@ -12,10 +12,6 @@ use UnexpectedValueException;
class ServeCommand
{
protected string $host;
protected int $port;
/**
* @var array<mixed>
*/
@ -25,10 +21,8 @@ class ServeCommand
protected CLImate $climate;
public function __construct(string $host = '127.0.0.1', int $port = 8000)
public function __construct(protected string $host = '127.0.0.1', protected int $port = 8000)
{
$this->host = $host;
$this->port = $port;
$this->climate = new CLImate();
}

View File

@ -20,7 +20,7 @@ class Config implements Arrayable
protected const INTERPOLATION_REGEX = '/\$(?!\$)\{([%a-z._]+)\}/i';
protected bool $resolved;
protected bool $resolved = false;
/**
* @param array<string, mixed> $data
@ -28,7 +28,6 @@ class Config implements Arrayable
public function __construct(array $data = [])
{
$this->data = $data;
$this->resolved = false;
}
public function get(string $key, mixed $default = null): mixed
@ -50,15 +49,8 @@ class Config implements Arrayable
{
if (FileSystem::isReadable($path) && FileSystem::extension($path) === 'yaml') {
$name = FileSystem::name($path);
$data = (array) Yaml::parseFile($path);
if (isset($this->data[$name])) {
$this->data[$name] = array_replace_recursive($this->data[$name], $data);
} else {
$this->data[$name] = $data;
}
$this->data[$name] = isset($this->data[$name]) ? array_replace_recursive($this->data[$name], $data) : $data;
}
}

View File

@ -10,9 +10,9 @@ use Formwork\Utils\FileSystem;
class AssetController
{
public function load(RouteParams $params, Config $config): FileResponse
public function load(RouteParams $routeParams, Config $config): FileResponse
{
$path = FileSystem::joinPaths($config->get('system.images.processPath'), $params->get('id'), $params->get('name'));
$path = FileSystem::joinPaths($config->get('system.images.processPath'), $routeParams->get('id'), $routeParams->get('name'));
if (FileSystem::isFile($path)) {
return new FileResponse($path);

View File

@ -18,12 +18,12 @@ use Formwork\View\ViewFactory;
class PageController extends AbstractController
{
public function __construct(protected App $app, protected Config $config, protected Router $router, protected Site $site, protected FilesCache $cache)
public function __construct(protected App $app, protected Config $config, protected Router $router, protected Site $site, protected FilesCache $filesCache)
{
parent::__construct();
}
public function load(RouteParams $params, ViewFactory $viewFactory): Response
public function load(RouteParams $routeParams, ViewFactory $viewFactory): Response
{
if ($this->site->get('maintenance.enabled') && !$this->app->panel()?->isLoggedIn()) {
if ($this->site->get('maintenance.page') !== null) {
@ -36,35 +36,35 @@ class PageController extends AbstractController
}
if (!isset($route)) {
$route = $params->get('page', $this->config->get('system.pages.index'));
$route = $routeParams->get('page', $this->config->get('system.pages.index'));
if ($resolvedAlias = $this->site->resolveRouteAlias($route)) {
$route = $resolvedAlias;
}
}
if ($page = $this->site->findPage($route)) {
if (($page = $this->site->findPage($route)) !== null) {
if ($page->canonicalRoute() !== null) {
$canonical = $page->canonicalRoute();
if ($params->get('page', '/') !== $canonical) {
if ($routeParams->get('page', '/') !== $canonical) {
$route = $this->router->rewrite(['page' => $canonical]);
return new RedirectResponse($this->site->uri($route), ResponseStatus::MovedPermanently);
}
}
if (($params->has('tagName') || $params->has('paginationPage')) && $page->scheme()->options()->get('type') !== 'listing') {
if (($routeParams->has('tagName') || $routeParams->has('paginationPage')) && $page->scheme()->options()->get('type') !== 'listing') {
return $this->getPageResponse($this->site->errorPage());
}
if ($this->config->get('system.cache.enabled') && ($page->has('publishDate') || $page->has('unpublishDate'))) {
if (($page->isPublished() && !$page->publishDate()->isEmpty() && !$this->site->modifiedSince($page->publishDate()->toTimestamp()))
|| (!$page->isPublished() && !$page->unpublishDate()->isEmpty() && !$this->site->modifiedSince($page->unpublishDate()->toTimestamp()))) {
// Clear cache if the site was not modified since the page has been published or unpublished
$this->cache->clear();
if ($this->site->path() !== null) {
FileSystem::touch($this->site->path());
}
if ($this->config->get('system.cache.enabled') && ($page->has('publishDate') || $page->has('unpublishDate')) && (
($page->isPublished() && !$page->publishDate()->isEmpty() && !$this->site->modifiedSince($page->publishDate()->toTimestamp()))
|| (!$page->isPublished() && !$page->unpublishDate()->isEmpty() && !$this->site->modifiedSince($page->unpublishDate()->toTimestamp()))
)) {
// Clear cache if the site was not modified since the page has been published or unpublished
$this->filesCache->clear();
if ($this->site->path() !== null) {
FileSystem::touch($this->site->path());
}
}
@ -79,7 +79,7 @@ class PageController extends AbstractController
$upperLevel = $this->config->get('system.pages.index');
}
if (($parent = $this->site->findPage($upperLevel)) && $parent->files()->has($filename)) {
if ((($parent = $this->site->findPage($upperLevel)) !== null) && $parent->files()->has($filename)) {
return new FileResponse($parent->files()->get($filename)->path());
}
}
@ -104,23 +104,23 @@ class PageController extends AbstractController
$cacheKey = $page->uri(includeLanguage: true);
if ($config->get('system.cache.enabled') && $this->cache->has($cacheKey)) {
if ($config->get('system.cache.enabled') && $this->filesCache->has($cacheKey)) {
/**
* @var int
*/
$cachedTime = $this->cache->cachedTime($cacheKey);
$cachedTime = $this->filesCache->cachedTime($cacheKey);
// Validate cached response
if (!$site->modifiedSince($cachedTime)) {
return $this->cache->fetch($cacheKey);
return $this->filesCache->fetch($cacheKey);
}
$this->cache->delete($cacheKey);
$this->filesCache->delete($cacheKey);
}
$response = new Response($page->render(), $page->responseStatus(), $page->headers());
if ($config->get('system.cache.enabled') && $page->cacheable()) {
$this->cache->save($cacheKey, $response);
$this->filesCache->save($cacheKey, $response);
}
return $response;

View File

@ -44,7 +44,7 @@ final class Collection extends AbstractCollection
*/
public static function create(array $data = [], ?string $dataType = null, bool $associative = false, bool $mutable = false): static
{
$collection = new static();
$collection = new self();
$collection->associative = $associative;
$collection->dataType = $dataType;

View File

@ -4,11 +4,8 @@ namespace Formwork\Data;
class CollectionDataProxy
{
protected AbstractCollection $collection;
public function __construct(AbstractCollection $collection)
public function __construct(protected AbstractCollection $collection)
{
$this->collection = $collection;
}
public function __get(string $name): Collection

View File

@ -31,14 +31,14 @@ final class DataGetter implements Arrayable
*/
public function isEmpty(): bool
{
return empty($this->data);
return $this->data === [];
}
/**
* Create an instance from another getter
*/
public static function fromGetter(DataGetter $getter): self
public static function fromGetter(DataGetter $dataGetter): self
{
return new static($getter->data);
return new self($dataGetter->data);
}
}

View File

@ -28,7 +28,7 @@ final class DataSetter implements Arrayable
*/
public function isEmpty(): bool
{
return empty($this->data);
return $this->data === [];
}
/**
@ -36,6 +36,6 @@ final class DataSetter implements Arrayable
*/
public static function fromGetter(DataGetter|DataSetter $getter): self
{
return new static($getter->toArray());
return new self($getter->toArray());
}
}

View File

@ -9,11 +9,6 @@ class Pagination
*/
protected int $count = 0;
/**
* Number of items in each pagination page
*/
protected int $length = 0;
/**
* Number of pagination pages
*/
@ -26,13 +21,13 @@ class Pagination
/**
* Create a new Pagination instance
*
* @param int $length Number of items in each pagination page
*/
public function __construct(AbstractCollection $collection, int $length)
public function __construct(AbstractCollection $collection, protected int $length)
{
$this->count = $collection->count();
$this->length = $length;
$this->pages = $this->count > 0 ? (int) ceil($this->count / $this->length) : 1;
}

View File

@ -30,15 +30,15 @@ class ErrorHandlers
/**
* Display error page
*/
public function displayErrorPage(ResponseStatus $status = ResponseStatus::InternalServerError): void
public function displayErrorPage(ResponseStatus $responseStatus = ResponseStatus::InternalServerError): void
{
Response::cleanOutputBuffers();
if ($this->request->isXmlHttpRequest()) {
JsonResponse::error('Error', $status)->send();
JsonResponse::error('Error', $responseStatus)->send();
} else {
$view = $this->viewFactory->make('errors.error', ['status' => $status->code(), 'message' => $status->message()]);
$response = new Response($view->render(), $status);
$view = $this->viewFactory->make('errors.error', ['status' => $responseStatus->code(), 'message' => $responseStatus->message()]);
$response = new Response($view->render(), $responseStatus);
$response->send();
// Don't exit, otherwise the error will not be logged
}
@ -47,16 +47,16 @@ class ErrorHandlers
/**
* Display error page on exception
*/
public function getExceptionHandler(Throwable $exception): void
public function getExceptionHandler(Throwable $throwable): void
{
static::displayErrorPage();
error_log(sprintf(
"Uncaught %s: %s in %s:%s\nStack trace:\n%s\n",
$exception::class,
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
$throwable::class,
$throwable->getMessage(),
$throwable->getFile(),
$throwable->getLine(),
$throwable->getTraceAsString()
));
}

View File

@ -7,23 +7,17 @@ use Formwork\App;
class TranslatedException extends Exception
{
/**
* Language string of the translated message
*/
protected string $languageString;
/**
* Create a new TranslatedException instance
*
* @param string $message Exception message
* @param string $languageString Language string of the translated message
* @param int $code Exception code
* @param Exception $previous Previous Exception
* @param string $message Exception message
* @param string $languageString Language string of the translated message
* @param int $code Exception code
* @param Exception $previousException Previous Exception
*/
public function __construct(string $message, string $languageString, int $code = 0, ?Exception $previous = null)
public function __construct(string $message, protected string $languageString, int $code = 0, ?Exception $previousException = null)
{
parent::__construct($message, $code, $previous);
$this->languageString = $languageString;
parent::__construct($message, $code, $previousException);
}
/**

View File

@ -23,31 +23,18 @@ class DynamicFieldValue
*/
protected bool $computing = false;
/**
* Dynamic value key
*/
protected string $key;
/**
* Uncomputed value
*/
protected string $uncomputedValue;
/**
* Field to which the value belongs
*/
protected Field $field;
/**
* Computed value
*/
protected mixed $value;
public function __construct(string $key, string $uncomputedValue, Field $field)
/**
* @param string $key Dynamic value key
* @param string $uncomputedValue Uncomputed value
* @param Field $field Field to which the value belongs
*/
public function __construct(protected string $key, protected string $uncomputedValue, protected Field $field)
{
$this->key = $key;
$this->uncomputedValue = $uncomputedValue;
$this->field = $field;
}
/**

View File

@ -15,9 +15,10 @@ use Formwork\Translations\Translation;
use Formwork\Utils\Arr;
use Formwork\Utils\Constraint;
use Formwork\Utils\Str;
use Stringable;
use UnexpectedValueException;
class Field implements Arrayable
class Field implements Arrayable, Stringable
{
use DataArrayable;
use DataMultipleGetter {
@ -31,16 +32,6 @@ class Field implements Arrayable
protected const UNTRANSLATABLE_KEYS = ['name', 'type', 'value', 'default', 'translate'];
/**
* Field name
*/
protected string $name;
/**
* Parent field collection
*/
protected ?FieldCollection $parent;
/**
* Field validation status
*/
@ -56,14 +47,12 @@ class Field implements Arrayable
/**
* Create a new Field instance
*
* @param string $name Field name
* @param array<string, mixed> $data
* @param ?FieldCollection $parentFieldCollection Parent field collection
*/
public function __construct(string $name, array $data = [], ?FieldCollection $parent = null)
public function __construct(protected string $name, array $data = [], protected ?FieldCollection $parentFieldCollection = null)
{
$this->name = $name;
$this->parent = $parent;
$this->setMultiple($data);
if ($this->has('fields')) {
@ -74,7 +63,7 @@ class Field implements Arrayable
public function __toString(): string
{
if ($this->hasMethod('toString')) {
return $this->callMethod('toString');
return (string) $this->callMethod('toString');
}
return (string) $this->value();
@ -93,7 +82,7 @@ class Field implements Arrayable
*/
public function parent(): ?FieldCollection
{
return $this->parent;
return $this->parentFieldCollection;
}
/**
@ -266,7 +255,7 @@ class Field implements Arrayable
}
if ($this->isTranslatable($key)) {
$value = $this->translate($value);
return $this->translate($value);
}
return $value;

View File

@ -17,9 +17,9 @@ class FieldFactory
/**
* @param array<string, mixed> $data
*/
public function make(string $name, array $data = [], ?FieldCollection $parent = null): Field
public function make(string $name, array $data = [], ?FieldCollection $parentFieldCollection = null): Field
{
$field = new Field($name, $data, $parent);
$field = new Field($name, $data, $parentFieldCollection);
$field->setTranslation($this->translations->getCurrent());

View File

@ -8,14 +8,10 @@ use Formwork\Utils\FileSystem;
use Formwork\Utils\MimeType;
use Formwork\Utils\Str;
use RuntimeException;
use Stringable;
class File implements Arrayable
class File implements Arrayable, Stringable
{
/**
* File path
*/
protected string $path;
/**
* File name
*/
@ -34,7 +30,7 @@ class File implements Arrayable
/**
* File type in a human-readable format
*/
protected ?string $type;
protected ?string $type = null;
/**
* File size in a human-readable format
@ -55,10 +51,11 @@ class File implements Arrayable
/**
* Create a new File instance
*
* @param string $path File path
*/
public function __construct(string $path)
public function __construct(protected string $path)
{
$this->path = $path;
$this->name = basename($path);
$this->extension = FileSystem::extension($path);
}
@ -105,7 +102,7 @@ class File implements Arrayable
*/
public function type(): ?string
{
if (isset($this->type)) {
if ($this->type !== null) {
return $this->type;
}
if (Str::startsWith($this->mimeType(), 'image')) {
@ -151,10 +148,7 @@ class File implements Arrayable
*/
public function lastModifiedTime(): int
{
if (isset($this->lastModifiedTime)) {
return $this->lastModifiedTime;
}
return FileSystem::lastModifiedTime($this->path);
return $this->lastModifiedTime ?? FileSystem::lastModifiedTime($this->path);
}
/**

View File

@ -25,17 +25,17 @@ class FileUploader
return Arr::map($this->config->get('system.files.allowedExtensions'), fn (string $ext) => MimeType::fromExtension($ext));
}
public function upload(UploadedFile $file, string $destinationPath, ?string $name = null): File
public function upload(UploadedFile $uploadedFile, string $destinationPath, ?string $name = null): File
{
$mimeType = MimeType::fromFile($file->tempPath());
$mimeType = MimeType::fromFile($uploadedFile->tempPath());
if (!in_array($mimeType, $this->allowedMimeTypes(), true)) {
throw new RuntimeException(sprintf('Invalid mime type %s for file uploads', $mimeType));
}
$filename = Str::slug($name ?? pathinfo($file->clientName(), PATHINFO_FILENAME)) . '.' . MimeType::toExtension($mimeType);
$filename = Str::slug($name ?? pathinfo($uploadedFile->clientName(), PATHINFO_FILENAME)) . '.' . MimeType::toExtension($mimeType);
$file->move($destinationPath, $filename);
$uploadedFile->move($destinationPath, $filename);
return new File(FileSystem::joinPaths($destinationPath, $filename));
}

View File

@ -10,14 +10,14 @@ class FileResponse extends Response
/**
* @inheritdoc
*/
public function __construct(string $path, ResponseStatus $status = ResponseStatus::OK, array $headers = [], bool $download = false)
public function __construct(string $path, ResponseStatus $responseStatus = ResponseStatus::OK, array $headers = [], bool $download = false)
{
$headers += [
'Content-Type' => FileSystem::mimeType($path),
'Content-Disposition' => !$download ? 'inline' : Header::make(['attachment', 'filename' => basename($path)]),
'Content-Disposition' => $download ? Header::make(['attachment', 'filename' => basename($path)]) : 'inline',
'Content-Length' => (string) FileSystem::fileSize($path),
];
parent::__construct(FileSystem::read($path), $status, $headers);
parent::__construct(FileSystem::read($path), $responseStatus, $headers);
}
/**

View File

@ -34,8 +34,6 @@ class UploadedFile
UPLOAD_ERR_EXTENSION => 'panel.uploader.error.phpExtension',
];
protected string $fieldName;
protected string $clientName;
protected string $clientFullPath;
@ -51,9 +49,8 @@ class UploadedFile
/**
* @param array{name: string, full_path: string, type: string, tmp_name: string, error: string, size: string} $data
*/
public function __construct(string $fieldName, array $data)
public function __construct(protected string $fieldName, array $data)
{
$this->fieldName = $fieldName;
$this->clientName = $data['name'];
$this->clientFullPath = $data['full_path'];
$this->clientMimeType = $data['type'];
@ -127,7 +124,7 @@ class UploadedFile
// throw new TranslatedException(sprintf('File "%s" already exists', $filename), 'panel.uploader.error.alreadyExists');
// }
if (move_uploaded_file($this->tempPath, $destinationPath) !== false) {
if (move_uploaded_file($this->tempPath, $destinationPath)) {
return true;
}

View File

@ -10,12 +10,12 @@ class JsonResponse extends Response
/**
* @inheritdoc
*/
public function __construct(string $data, ResponseStatus $status = ResponseStatus::OK, array $headers = [])
public function __construct(string $data, ResponseStatus $responseStatus = ResponseStatus::OK, array $headers = [])
{
$headers += [
'Content-Type' => Header::make(['application/json', 'charset' => 'utf-8']),
];
parent::__construct($data, $status, $headers);
parent::__construct($data, $responseStatus, $headers);
}
/**
@ -23,14 +23,14 @@ class JsonResponse extends Response
*
* @param array<mixed> $data
*/
public static function success(string $message, ResponseStatus $status = ResponseStatus::OK, array $data = []): self
public static function success(string $message, ResponseStatus $responseStatus = ResponseStatus::OK, array $data = []): self
{
return new static(Json::encode([
'status' => 'success',
'message' => $message,
'code' => $status,
'code' => $responseStatus,
'data' => $data,
]), $status);
]), $responseStatus);
}
/**
@ -38,13 +38,13 @@ class JsonResponse extends Response
*
* @param array<mixed> $data
*/
public static function error(string $message, ResponseStatus $status = ResponseStatus::BadRequest, array $data = []): self
public static function error(string $message, ResponseStatus $responseStatus = ResponseStatus::BadRequest, array $data = []): self
{
return new static(Json::encode([
'status' => 'error',
'message' => $message,
'code' => $status,
'code' => $responseStatus,
'data' => $data,
]), $status);
]), $responseStatus);
}
}

View File

@ -7,11 +7,11 @@ class RedirectResponse extends Response
/**
* @inheritdoc
*/
public function __construct(string $uri, ResponseStatus $status = ResponseStatus::Found, array $headers = [])
public function __construct(string $uri, ResponseStatus $responseStatus = ResponseStatus::Found, array $headers = [])
{
$headers += [
'Location' => $uri,
];
parent::__construct('', $status, $headers);
parent::__construct('', $responseStatus, $headers);
}
}

View File

@ -393,7 +393,7 @@ class Request
foreach ($files as $fieldName => $data) {
if (is_array($data['name'])) {
foreach ($data['name'] as $i => $name) {
foreach (array_keys($data['name']) as $i) {
/**
* @var array<string, list<UploadedFile>> $result
*/

View File

@ -6,16 +6,6 @@ use Formwork\Http\Utils\Header;
class Response implements ResponseInterface
{
/**
* Response content
*/
protected string $content;
/**
* Response HTTP status
*/
protected ResponseStatus $status;
/**
* Response HTTP headers
*
@ -25,15 +15,15 @@ class Response implements ResponseInterface
/**
* Create a new Response instance
*
* @param string $content Response content
* @param ResponseStatus $responseStatus Response HTTP status
*/
public function __construct(string $content, ResponseStatus $status = ResponseStatus::OK, array $headers = [])
public function __construct(protected string $content, protected ResponseStatus $responseStatus = ResponseStatus::OK, array $headers = [])
{
$headers += [
'Content-Type' => Header::make(['text/html', 'charset' => 'utf-8']),
];
$this->content = $content;
$this->status = $status;
$this->headers = $headers;
}
@ -55,7 +45,7 @@ class Response implements ResponseInterface
*/
public function status(): ResponseStatus
{
return $this->status;
return $this->responseStatus;
}
/**
@ -71,7 +61,7 @@ class Response implements ResponseInterface
*/
public function sendStatus(): void
{
Header::status($this->status);
Header::status($this->responseStatus);
}
/**
@ -99,7 +89,7 @@ class Response implements ResponseInterface
{
return [
'content' => $this->content,
'status' => $this->status,
'status' => $this->responseStatus,
'headers' => $this->headers,
];
}

View File

@ -11,7 +11,7 @@ interface ResponseInterface extends ArraySerializable
*
* @param array<string, string> $headers
*/
public function __construct(string $content, ResponseStatus $status = ResponseStatus::OK, array $headers = []);
public function __construct(string $content, ResponseStatus $responseStatus = ResponseStatus::OK, array $headers = []);
/**
* @param array{content: string, status: ResponseStatus, headers: array<string, string>} $properties

View File

@ -22,18 +22,18 @@ class Messages implements Arrayable
$this->data = &$data;
}
public function has(MessageType $type): bool
public function has(MessageType $messageType): bool
{
return !empty($this->data[$type->value]);
return !empty($this->data[$messageType->value]);
}
/**
* @return list<string>
*/
public function get(MessageType $type): array
public function get(MessageType $messageType): array
{
$messages = $this->data[$type->value] ?? [];
$this->remove($type);
$messages = $this->data[$messageType->value] ?? [];
$this->remove($messageType);
return $messages;
}
@ -50,22 +50,22 @@ class Messages implements Arrayable
/**
* @param list<string>|string $messages
*/
public function set(MessageType $type, string|array $messages): void
public function set(MessageType $messageType, string|array $messages): void
{
$this->data[$type->value] = (array) $messages;
$this->data[$messageType->value] = (array) $messages;
}
public function add(MessageType $type, string $message): void
public function add(MessageType $messageType, string $message): void
{
if (empty($this->data[$type->value])) {
$this->set($type, []);
if (empty($this->data[$messageType->value])) {
$this->set($messageType, []);
}
$this->data[$type->value][] = $message;
$this->data[$messageType->value][] = $message;
}
public function remove(MessageType $type): void
public function remove(MessageType $messageType): void
{
unset($this->data[$type->value]);
unset($this->data[$messageType->value]);
}
public function removeAll(): void

View File

@ -30,8 +30,6 @@ class Session implements Arrayable
protected const SESSION_ID_REGEX = '/^[a-z0-9,-]{22,256}$/i';
protected Request $request;
protected Messages $messages;
protected string $name = self::SESSION_NAME;
@ -40,7 +38,7 @@ class Session implements Arrayable
protected int $duration = 0;
public function __construct(Request $request)
public function __construct(protected Request $request)
{
if (!extension_loaded('session')) {
throw new RuntimeException('Sessions extension not available');
@ -49,8 +47,6 @@ class Session implements Arrayable
if (session_status() === PHP_SESSION_DISABLED) {
throw new RuntimeException('Sessions disabled by PHP configuration');
}
$this->request = $request;
}
public function exists(string $id): bool

View File

@ -20,14 +20,14 @@ class Header
*
* @return string|void
*/
public static function status(ResponseStatus $status, bool $send = true, bool $exit = false)
public static function status(ResponseStatus $responseStatus, bool $send = true, bool $exit = false)
{
$protocol = $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0';
$status = implode(' ', [$protocol, $status->value]);
$responseStatus = implode(' ', [$protocol, $responseStatus->value]);
if (!$send) {
return $status;
return $responseStatus;
}
header($status);
header($responseStatus);
if ($exit) {
exit;
}
@ -65,14 +65,14 @@ class Header
/**
* Redirect to a given URI and exit from the script
*
* @param ResponseStatus $status Redirect HTTP response status code
* @param ResponseStatus $responseStatus Redirect HTTP response status code
*/
public static function redirect(string $uri, ResponseStatus $status = ResponseStatus::Found): void
public static function redirect(string $uri, ResponseStatus $responseStatus = ResponseStatus::Found): void
{
if ($status->type() !== ResponseStatusType::Redirection) {
throw new InvalidArgumentException(sprintf('Invalid response status "%s" for redirection, only 3XX statuses are allowed', $status->value));
if ($responseStatus->type() !== ResponseStatusType::Redirection) {
throw new InvalidArgumentException(sprintf('Invalid response status "%s" for redirection, only 3XX statuses are allowed', $responseStatus->value));
}
static::status($status);
static::status($responseStatus);
static::send('Location', $uri);
exit;
}

View File

@ -24,14 +24,11 @@ class IpAnonymizer
*/
public static function anonymize(string $ip): string
{
switch (strlen(self::packIPAddress($ip))) {
case 4:
return static::anonymizeIPv4($ip);
case 16:
return static::anonymizeIPv6($ip);
default:
throw new InvalidArgumentException(sprintf('Invalid IP address %s', $ip));
}
return match (strlen(self::packIPAddress($ip))) {
4 => static::anonymizeIPv4($ip),
16 => static::anonymizeIPv6($ip),
default => throw new InvalidArgumentException(sprintf('Invalid IP address %s', $ip)),
};
}
/**

View File

@ -12,17 +12,13 @@ class ColorProfile
protected const ICC_PROFILE_SIGNATURE_OFFSET = 36;
protected string $data;
/**
* @var array<string, mixed>
*/
protected array $tags;
public function __construct(string $data)
public function __construct(protected string $data)
{
$this->data = $data;
if (strpos($this->data, self::ICC_PROFILE_SIGNATURE) !== self::ICC_PROFILE_SIGNATURE_OFFSET) {
throw new InvalidArgumentException('Invalid ICC profile data');
}

View File

@ -9,17 +9,14 @@ class ExifData implements Arrayable
{
protected ExifReader $reader;
protected string $data;
/**
* @var array<string, mixed>
*/
protected array $tags;
public function __construct(string $data)
public function __construct(protected string $data)
{
$this->reader = new ExifReader();
$this->data = $data;
$this->tags = $this->reader->read($this->data);
}

View File

@ -3,8 +3,9 @@
namespace Formwork\Images\Exif;
use DateTimeImmutable;
use Stringable;
class ExifDateTime extends DateTimeImmutable
class ExifDateTime extends DateTimeImmutable implements Stringable
{
public const EXIF = 'Y:m:d H:i:s';

View File

@ -14,8 +14,6 @@ use UnexpectedValueException;
abstract class AbstractHandler implements HandlerInterface
{
protected string $data;
protected DecoderInterface $decoder;
/**
@ -26,9 +24,8 @@ abstract class AbstractHandler implements HandlerInterface
/**
* @param array<string, mixed> $options
*/
public function __construct(string $data, array $options = [])
public function __construct(protected string $data, array $options = [])
{
$this->data = $data;
$this->decoder = $this->getDecoder();
$this->options = [...$this->defaults(), ...$options];
}
@ -38,11 +35,11 @@ abstract class AbstractHandler implements HandlerInterface
return new static(FileSystem::read($path));
}
public static function fromGdImage(GdImage $image, array $options = []): static
public static function fromGdImage(GdImage $gdImage, array $options = []): static
{
$handler = new static('', $options);
$handler->setDataFromGdImage($image);
return $handler;
$static = new static('', $options);
$static->setDataFromGdImage($gdImage);
return $static;
}
/**
@ -69,7 +66,7 @@ abstract class AbstractHandler implements HandlerInterface
*
* @throws RuntimeException if the image has no color profile
*/
abstract public function setColorProfile(ColorProfile $profile): void;
abstract public function setColorProfile(ColorProfile $colorProfile): void;
/**
* Remove color profile
@ -97,7 +94,7 @@ abstract class AbstractHandler implements HandlerInterface
*
* @throws RuntimeException if the image does not support EXIF data
*/
abstract public function setExifData(ExifData $data): void;
abstract public function setExifData(ExifData $exifData): void;
/**
* Remove EXIF data
@ -137,19 +134,19 @@ abstract class AbstractHandler implements HandlerInterface
];
}
public function process(?TransformCollection $transforms = null, ?string $handler = null): AbstractHandler
public function process(?TransformCollection $transformCollection = null, ?string $handler = null): AbstractHandler
{
$handler ??= $this::class;
$handler ??= static::class;
if (!is_subclass_of($handler, self::class)) {
throw new UnexpectedValueException(sprintf('Invalid handler of type %s, only instances of %s are allowed', get_debug_type($handler), self::class));
}
if ($handler === $this::class && $transforms === null) {
if ($handler === static::class && $transformCollection === null) {
return $this;
}
$info = $this->getInfo();
$imageInfo = $this->getInfo();
if ($this->options['preserveColorProfile'] && $this->hasColorProfile() && $handler::supportsColorProfile()) {
$colorProfile = $this->getColorProfile();
@ -161,13 +158,13 @@ abstract class AbstractHandler implements HandlerInterface
$image = $this->toGdImage();
if ($transforms !== null) {
foreach ($transforms as $transform) {
$image = $transform->apply($image, $info);
if ($transformCollection !== null) {
foreach ($transformCollection as $transform) {
$image = $transform->apply($image, $imageInfo);
}
}
if ($handler === $this::class) {
if ($handler === static::class) {
$this->setDataFromGdImage($image);
$instance = $this;
} else {
@ -193,7 +190,7 @@ abstract class AbstractHandler implements HandlerInterface
*/
abstract protected function getDecoder(): DecoderInterface;
abstract protected function setDataFromGdImage(GdImage $image): void;
abstract protected function setDataFromGdImage(GdImage $gdImage): void;
protected function toGdImage(): GdImage
{

View File

@ -50,10 +50,13 @@ class GifHandler extends AbstractHandler
$info['animationRepeatCount']++;
}
}
if ($block['type'] === 'IMG' && $info['isAnimation']) {
$info['animationFrames']++;
if ($block['type'] !== 'IMG') {
continue;
}
if (!$info['isAnimation']) {
continue;
}
$info['animationFrames']++;
}
return new ImageInfo($info);
@ -74,7 +77,7 @@ class GifHandler extends AbstractHandler
throw new UnsupportedFeatureException('GIF does not support color profiles');
}
public function setColorProfile(ColorProfile $profile): void
public function setColorProfile(ColorProfile $colorProfile): void
{
throw new UnsupportedFeatureException('GIF does not support color profiles');
}
@ -99,7 +102,7 @@ class GifHandler extends AbstractHandler
throw new UnsupportedFeatureException('GIF does not support EXIF data');
}
public function setExifData(ExifData $data): void
public function setExifData(ExifData $exifData): void
{
throw new UnsupportedFeatureException('GIF does not support EXIF data');
}
@ -114,13 +117,13 @@ class GifHandler extends AbstractHandler
return new GifDecoder();
}
protected function setDataFromGdImage(GdImage $image): void
protected function setDataFromGdImage(GdImage $gdImage): void
{
imagetruecolortopalette($image, true, $this->options['gifColors']);
imagetruecolortopalette($gdImage, true, $this->options['gifColors']);
ob_start();
if (imagegif($image, null) === false) {
if (imagegif($gdImage, null) === false) {
throw new RuntimeException('Cannot set data from GdImage');
}

View File

@ -21,7 +21,7 @@ interface HandlerInterface
/**
* @param array<string, mixed> $options
*/
public static function fromGdImage(GdImage $image, array $options = []): HandlerInterface;
public static function fromGdImage(GdImage $gdImage, array $options = []): HandlerInterface;
/**
* Get image info as an array
@ -47,7 +47,7 @@ interface HandlerInterface
*
* @throws RuntimeException if the image has no color profile
*/
public function setColorProfile(ColorProfile $profile): void;
public function setColorProfile(ColorProfile $colorProfile): void;
/**
* Remove color profile
@ -75,7 +75,7 @@ interface HandlerInterface
*
* @throws RuntimeException if the image does not support EXIF data
*/
public function setExifData(ExifData $data): void;
public function setExifData(ExifData $exifData): void;
/**
* Remove EXIF data
@ -98,5 +98,5 @@ interface HandlerInterface
*/
public function defaults(): array;
public function process(?TransformCollection $transforms = null, ?string $handler = null): self;
public function process(?TransformCollection $transformCollection = null, ?string $handler = null): self;
}

View File

@ -56,9 +56,13 @@ class JpegHandler extends AbstractHandler
public function hasColorProfile(): bool
{
foreach ($this->decoder->decode($this->data) as $segment) {
if ($segment['type'] === 0xe2 && str_starts_with($segment['value'], self::ICC_PROFILE_HEADER)) {
return true;
if ($segment['type'] !== 0xe2) {
continue;
}
if (!str_starts_with($segment['value'], self::ICC_PROFILE_HEADER)) {
continue;
}
return true;
}
return false;
@ -89,11 +93,11 @@ class JpegHandler extends AbstractHandler
return new ColorProfile(implode('', $profileChunks));
}
public function setColorProfile(ColorProfile $profile): void
public function setColorProfile(ColorProfile $colorProfile): void
{
foreach ($this->decoder->decode($this->data) as $segment) {
if ($segment['type'] === 0xd8) {
$this->data = substr_replace($this->data, $this->encodeColorProfile($profile->getData()), $segment['position'], 0);
$this->data = substr_replace($this->data, $this->encodeColorProfile($colorProfile->getData()), $segment['position'], 0);
break;
}
}
@ -117,9 +121,13 @@ class JpegHandler extends AbstractHandler
public function hasExifData(): bool
{
foreach ($this->decoder->decode($this->data) as $segment) {
if ($segment['type'] === 0xe1 && str_starts_with($segment['value'], self::EXIF_HEADER)) {
return true;
if ($segment['type'] !== 0xe1) {
continue;
}
if (!str_starts_with($segment['value'], self::EXIF_HEADER)) {
continue;
}
return true;
}
return false;
}
@ -127,18 +135,22 @@ class JpegHandler extends AbstractHandler
public function getExifData(): ?ExifData
{
foreach ($this->decoder->decode($this->data) as $segment) {
if ($segment['type'] === 0xe1 && str_starts_with($segment['value'], self::EXIF_HEADER)) {
return new ExifData(substr($segment['value'], strlen(self::EXIF_HEADER)));
if ($segment['type'] !== 0xe1) {
continue;
}
if (!str_starts_with($segment['value'], self::EXIF_HEADER)) {
continue;
}
return new ExifData(substr($segment['value'], strlen(self::EXIF_HEADER)));
}
return null;
}
public function setExifData(ExifData $data): void
public function setExifData(ExifData $exifData): void
{
foreach ($this->decoder->decode($this->data) as $segment) {
if ($segment['type'] === 0xd8) {
$this->data = substr_replace($this->data, $this->encodeExifData($data->getData()), $segment['position'], 0);
$this->data = substr_replace($this->data, $this->encodeExifData($exifData->getData()), $segment['position'], 0);
break;
}
}
@ -189,13 +201,13 @@ class JpegHandler extends AbstractHandler
return new JpegDecoder();
}
protected function setDataFromGdImage(GdImage $image): void
protected function setDataFromGdImage(GdImage $gdImage): void
{
imageinterlace($image, $this->options['jpegProgressive']);
imageinterlace($gdImage, $this->options['jpegProgressive']);
ob_start();
if (imagejpeg($image, null, $this->options['jpegQuality']) === false) {
if (imagejpeg($gdImage, null, $this->options['jpegQuality']) === false) {
throw new RuntimeException('Cannot set data from GdImage');
}

View File

@ -81,11 +81,11 @@ class PngHandler extends AbstractHandler
return null;
}
public function setColorProfile(ColorProfile $profile): void
public function setColorProfile(ColorProfile $colorProfile): void
{
foreach ($this->decoder->decode($this->data) as $chunk) {
if ($chunk['type'] === 'IHDR') {
$iCCPChunk = $this->encodeChunk('iCCP', $this->encodeProfile($profile->name(), $profile->getData()));
$iCCPChunk = $this->encodeChunk('iCCP', $this->encodeProfile($colorProfile->name(), $colorProfile->getData()));
$this->data = substr_replace($this->data, $iCCPChunk, $chunk['position'], 0);
break;
}
@ -130,11 +130,11 @@ class PngHandler extends AbstractHandler
return null;
}
public function setExifData(ExifData $data): void
public function setExifData(ExifData $exifData): void
{
foreach ($this->decoder->decode($this->data) as $chunk) {
if ($chunk['type'] === 'IHDR') {
$iCCPChunk = $this->encodeChunk('eXIf', $data->getData());
$iCCPChunk = $this->encodeChunk('eXIf', $exifData->getData());
$this->data = substr_replace($this->data, $iCCPChunk, $chunk['position'], 0);
break;
}
@ -192,11 +192,11 @@ class PngHandler extends AbstractHandler
return new PngDecoder();
}
protected function setDataFromGdImage(GdImage $image): void
protected function setDataFromGdImage(GdImage $gdImage): void
{
ob_start();
if (imagepng($image, null, $this->options['pngCompression']) === false) {
if (imagepng($gdImage, null, $this->options['pngCompression']) === false) {
throw new RuntimeException('Cannot set data from GdImage');
}

View File

@ -68,10 +68,13 @@ class WebpHandler extends AbstractHandler
$info['isAnimation'] = true;
$info['animationRepeatCount'] = unpack('v', $chunk['value'], 4)[1];
}
if ($info['isAnimation'] && $chunk['type'] === 'ANMF') {
$info['animationFrames']++;
if (!$info['isAnimation']) {
continue;
}
if ($chunk['type'] !== 'ANMF') {
continue;
}
$info['animationFrames']++;
}
return new ImageInfo($info);
@ -104,13 +107,13 @@ class WebpHandler extends AbstractHandler
return null;
}
public function setColorProfile(ColorProfile $profile): void
public function setColorProfile(ColorProfile $colorProfile): void
{
foreach ($this->decoder->decode($this->data) as $chunk) {
if ($chunk['type'] === 'VP8X') {
$VP8XFlags = ord($chunk['value'][0]) | self::ICC_FLAG;
$this->data = substr_replace($this->data, chr($VP8XFlags), $chunk['offset'] + 8, 1);
$ICCPChunk = $this->encodeChunk('ICCP', $profile->getData());
$ICCPChunk = $this->encodeChunk('ICCP', $colorProfile->getData());
$this->data = substr_replace($this->data, $ICCPChunk, $chunk['position'], 0);
$this->updateRIFFHeader();
break;
@ -161,7 +164,7 @@ class WebpHandler extends AbstractHandler
return null;
}
public function setExifData(ExifData $data): void
public function setExifData(ExifData $exifData): void
{
foreach ($this->decoder->decode($this->data) as $chunk) {
if ($chunk['type'] === 'VP8X') {
@ -170,7 +173,7 @@ class WebpHandler extends AbstractHandler
}
if (in_array($chunk['type'], ['VP8 ', 'VP8L'], true)) {
$ExifChunk = $this->encodeChunk('EXIF', $data->getData());
$ExifChunk = $this->encodeChunk('EXIF', $exifData->getData());
$this->data = substr_replace($this->data, $ExifChunk, $chunk['position'], 0);
$this->updateRIFFHeader();
}
@ -213,11 +216,11 @@ class WebpHandler extends AbstractHandler
return new WebpDecoder();
}
protected function setDataFromGdImage(GdImage $image): void
protected function setDataFromGdImage(GdImage $gdImage): void
{
ob_start();
if (imagewebp($image, null, $this->options['webpQuality']) === false) {
if (imagewebp($gdImage, null, $this->options['webpQuality']) === false) {
throw new RuntimeException('Cannot set data from GdImage');
}
@ -228,7 +231,7 @@ class WebpHandler extends AbstractHandler
protected function setVP8XChunk(): void
{
if (strpos($this->data, 'VP8X', 12) === false) {
if (!str_contains(substr($this->data, 12), 'VP8X')) {
$info = $this->getInfo();
$data = chr(self::ALPHA_FLAG) . "\x0\x0\x0" . substr(pack('V', $info->width() - 1), 0, 3) . substr(pack('V', $info->height() - 1), 0, 3);
$chunk = $this->encodeChunk('VP8X', $data);

View File

@ -39,11 +39,6 @@ class Image extends File
{
protected string $path;
/**
* @var array<string, mixed>
*/
protected array $options = [];
protected AbstractHandler $handler;
protected ImageInfo $info;
@ -57,10 +52,9 @@ class Image extends File
/**
* @param array<string, mixed> $options
*/
public function __construct(string $path, array $options)
public function __construct(string $path, protected array $options)
{
parent::__construct($path);
$this->options = $options;
$this->transforms = new TransformCollection();
}
@ -140,9 +134,9 @@ class Image extends File
return $this;
}
public function blur(int $amount, BlurMode $mode = BlurMode::Mean): self
public function blur(int $amount, BlurMode $blurMode = BlurMode::Mean): self
{
$this->transforms->add(new Blur($amount, $mode));
$this->transforms->add(new Blur($amount, $blurMode));
return $this;
}
@ -229,9 +223,9 @@ class Image extends File
*
* @throws RuntimeException if the image has no color profile
*/
public function setColorProfile(ColorProfile $profile): void
public function setColorProfile(ColorProfile $colorProfile): void
{
$this->handler()->setColorProfile($profile);
$this->handler()->setColorProfile($colorProfile);
}
/**
@ -267,9 +261,9 @@ class Image extends File
*
* @throws RuntimeException if the image does not support EXIF data
*/
public function setExifData(ExifData $data): void
public function setExifData(ExifData $exifData): void
{
$this->handler()->setExifData($data);
$this->handler()->setExifData($exifData);
}
/**
@ -357,7 +351,13 @@ class Image extends File
public function toArray(): array
{
return [...parent::toArray(), 'imageInfo' => $this->info()->toArray(), 'exif' => $this->getExifData()?->toArray(), 'colorProfile' => $this->getColorProfile()?->name(), 'uri' => $this->uri()];
return [
...parent::toArray(),
'imageInfo' => $this->info()->toArray(),
'exif' => $this->getExifData()?->toArray(),
'colorProfile' => $this->getColorProfile()?->name(),
'uri' => $this->uri(),
];
}
protected function getHash(?string $mimeType = null): string

View File

@ -14,19 +14,19 @@ class ImageInfo implements Arrayable
protected int $height;
protected ?ColorSpace $colorSpace;
protected ?ColorSpace $colorSpace = null;
protected ?int $colorDepth;
protected ?int $colorDepth = null;
protected ?int $colorNumber;
protected ?int $colorNumber = null;
protected bool $hasAlphaChannel;
protected bool $isAnimation;
protected ?int $animationFrames;
protected ?int $animationFrames = null;
protected ?int $animationRepeatCount;
protected ?int $animationRepeatCount = null;
/**
* @param array<string, mixed> $info

View File

@ -36,18 +36,18 @@ class Blur extends AbstractTransform
protected BlurMode $mode;
final public function __construct(int $amount, BlurMode $mode)
final public function __construct(int $amount, BlurMode $blurMode)
{
if (!Constraint::isInIntegerRange($amount, 0, 100)) {
throw new InvalidArgumentException(sprintf('$amount value must be in range 0-100, %d given', $amount));
}
if (!isset(self::CONVOLUTION_KERNELS[$mode->name])) {
throw new InvalidArgumentException(sprintf('Invalid blur mode, "%s" given', $mode->name));
if (!isset(self::CONVOLUTION_KERNELS[$blurMode->name])) {
throw new InvalidArgumentException(sprintf('Invalid blur mode, "%s" given', $blurMode->name));
}
$this->amount = $amount;
$this->mode = $mode;
$this->mode = $blurMode;
}
public static function fromArray(array $data): static
@ -55,12 +55,12 @@ class Blur extends AbstractTransform
return new static($data['amount'], $data['mode']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
for ($i = 0; $i < $this->amount; $i++) {
imageconvolution($image, self::CONVOLUTION_KERNELS[$this->mode->name], 1, 0.55);
imageconvolution($gdImage, self::CONVOLUTION_KERNELS[$this->mode->name], 1, 0.55);
}
return $image;
return $gdImage;
}
}

View File

@ -27,9 +27,9 @@ class Brightness extends AbstractTransform
return new static($data['amount']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_BRIGHTNESS, $this->amount);
return $image;
imagefilter($gdImage, IMG_FILTER_BRIGHTNESS, $this->amount);
return $gdImage;
}
}

View File

@ -46,9 +46,9 @@ class Colorize extends AbstractTransform
return new static($data['red'], $data['green'], $data['blue'], $data['alpha']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_COLORIZE, $this->red, $this->green, $this->blue, $this->alpha);
return $image;
imagefilter($gdImage, IMG_FILTER_COLORIZE, $this->red, $this->green, $this->blue, $this->alpha);
return $gdImage;
}
}

View File

@ -25,10 +25,10 @@ class Contrast extends AbstractTransform
return new static($data['amount']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
// For GD -100 = max contrast, 100 = min contrast; we change $amount sign for a more predictable behavior
imagefilter($image, IMG_FILTER_CONTRAST, -$this->amount);
return $image;
imagefilter($gdImage, IMG_FILTER_CONTRAST, -$this->amount);
return $gdImage;
}
}

View File

@ -7,20 +7,8 @@ use GdImage;
class Crop extends AbstractTransform
{
protected int $originX;
protected int $originY;
protected int $width;
protected int $height;
final public function __construct(int $originX, int $originY, int $width, int $height)
final public function __construct(protected int $originX, protected int $originY, protected int $width, protected int $height)
{
$this->originX = $originX;
$this->originY = $originY;
$this->width = $width;
$this->height = $height;
}
public static function fromArray(array $data): static
@ -28,7 +16,7 @@ class Crop extends AbstractTransform
return new static($data['originX'], $data['originY'], $data['width'], $data['height']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
$destinationImage = imagecreatetruecolor($this->width, $this->height);
@ -36,7 +24,7 @@ class Crop extends AbstractTransform
imagecopy(
$destinationImage,
$image,
$gdImage,
0,
0,
$this->originX,
@ -48,12 +36,12 @@ class Crop extends AbstractTransform
return $destinationImage;
}
protected function enableTransparency(GdImage $image): void
protected function enableTransparency(GdImage $gdImage): void
{
$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
imagealphablending($image, true);
imagesavealpha($image, true);
imagecolortransparent($image, $transparent);
imagefill($image, 0, 0, $transparent);
$transparent = imagecolorallocatealpha($gdImage, 0, 0, 0, 127);
imagealphablending($gdImage, true);
imagesavealpha($gdImage, true);
imagecolortransparent($gdImage, $transparent);
imagefill($gdImage, 0, 0, $transparent);
}
}

View File

@ -17,9 +17,9 @@ class Desaturate extends AbstractTransform
return new static();
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_GRAYSCALE);
return $image;
imagefilter($gdImage, IMG_FILTER_GRAYSCALE);
return $gdImage;
}
}

View File

@ -17,9 +17,9 @@ class EdgeDetect extends AbstractTransform
return new static();
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_EDGEDETECT);
return $image;
imagefilter($gdImage, IMG_FILTER_EDGEDETECT);
return $gdImage;
}
}

View File

@ -17,9 +17,9 @@ class Emboss extends AbstractTransform
return new static();
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_EMBOSS);
return $image;
imagefilter($gdImage, IMG_FILTER_EMBOSS);
return $gdImage;
}
}

View File

@ -16,13 +16,13 @@ class Flip extends AbstractTransform
protected FlipDirection $direction;
final public function __construct(FlipDirection $direction)
final public function __construct(FlipDirection $flipDirection)
{
if (!isset(self::DIRECTIONS[$direction->name])) {
throw new InvalidArgumentException(sprintf('Invalid flip direction, "%s" given', $direction->name));
if (!isset(self::DIRECTIONS[$flipDirection->name])) {
throw new InvalidArgumentException(sprintf('Invalid flip direction, "%s" given', $flipDirection->name));
}
$this->direction = $direction;
$this->direction = $flipDirection;
}
public static function fromArray(array $data): static
@ -30,9 +30,9 @@ class Flip extends AbstractTransform
return new static($data['direction']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imageflip($image, self::DIRECTIONS[$this->direction->name]);
return $image;
imageflip($gdImage, self::DIRECTIONS[$this->direction->name]);
return $gdImage;
}
}

View File

@ -17,9 +17,9 @@ class Invert extends AbstractTransform
return new static();
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_NEGATE);
return $image;
imagefilter($gdImage, IMG_FILTER_NEGATE);
return $gdImage;
}
}

View File

@ -7,11 +7,8 @@ use GdImage;
class Pixelate extends AbstractTransform
{
protected int $amount;
final public function __construct(int $amount)
final public function __construct(protected int $amount)
{
$this->amount = $amount;
}
public static function fromArray(array $data): static
@ -19,9 +16,9 @@ class Pixelate extends AbstractTransform
return new static($data['amount']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_PIXELATE, $this->amount);
return $image;
imagefilter($gdImage, IMG_FILTER_PIXELATE, $this->amount);
return $gdImage;
}
}

View File

@ -7,17 +7,8 @@ use GdImage;
class Resize extends AbstractTransform
{
protected int $width;
protected int $height;
protected ResizeMode $mode;
final public function __construct(int $width, int $height, ResizeMode $mode = ResizeMode::Cover)
final public function __construct(protected int $width, protected int $height, protected ResizeMode $resizeMode = ResizeMode::Cover)
{
$this->width = $width;
$this->height = $height;
$this->mode = $mode;
}
public static function fromArray(array $data): static
@ -25,10 +16,10 @@ class Resize extends AbstractTransform
return new static($data['width'], $data['height'], $data['mode']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
$sourceWidth = imagesx($image);
$sourceHeight = imagesy($image);
$sourceWidth = imagesx($gdImage);
$sourceHeight = imagesy($gdImage);
$cropAreaWidth = $sourceWidth;
$cropAreaHeight = $sourceHeight;
@ -48,7 +39,7 @@ class Resize extends AbstractTransform
$width = $this->width;
$height = $this->height;
switch ($this->mode) {
switch ($this->resizeMode) {
case ResizeMode::Fill:
$cropAreaWidth = $sourceWidth;
$cropAreaHeight = $sourceHeight;
@ -87,13 +78,13 @@ class Resize extends AbstractTransform
$destinationImage = imagecreatetruecolor((int) $width, (int) $height);
if ($info->hasAlphaChannel()) {
if ($imageInfo->hasAlphaChannel()) {
$this->enableTransparency($destinationImage);
}
imagecopyresampled(
$destinationImage,
$image,
$gdImage,
(int) $destinationX,
(int) $destinationY,
(int) $cropOriginX,
@ -107,12 +98,12 @@ class Resize extends AbstractTransform
return $destinationImage;
}
protected function enableTransparency(GdImage $image): void
protected function enableTransparency(GdImage $gdImage): void
{
$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
imagealphablending($image, true);
imagesavealpha($image, true);
imagecolortransparent($image, $transparent);
imagefill($image, 0, 0, $transparent);
$transparent = imagecolorallocatealpha($gdImage, 0, 0, 0, 127);
imagealphablending($gdImage, true);
imagesavealpha($gdImage, true);
imagecolortransparent($gdImage, $transparent);
imagefill($gdImage, 0, 0, $transparent);
}
}

View File

@ -7,11 +7,8 @@ use GdImage;
class Rotate extends AbstractTransform
{
protected float $angle;
final public function __construct(float $angle)
final public function __construct(protected float $angle)
{
$this->angle = $angle;
}
public static function fromArray(array $data): static
@ -19,10 +16,9 @@ class Rotate extends AbstractTransform
return new static($data['angle']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
$backgroundColor = imagecolorallocatealpha($image, 0, 0, 0, 127);
$image = imagerotate($image, $this->angle, $backgroundColor);
return $image;
$backgroundColor = imagecolorallocatealpha($gdImage, 0, 0, 0, 127);
return imagerotate($gdImage, $this->angle, $backgroundColor);
}
}

View File

@ -7,11 +7,8 @@ use GdImage;
class Scale extends AbstractTransform
{
protected float $factor;
final public function __construct(float $factor)
final public function __construct(protected float $factor)
{
$this->factor = $factor;
}
public static function fromArray(array $data): static
@ -19,9 +16,9 @@ class Scale extends AbstractTransform
return new static($data['factor']);
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
$resize = new Resize((int) floor(imagesx($image) * $this->factor), (int) floor(imagesy($image) * $this->factor));
return $resize->apply($image, $info);
$resize = new Resize((int) floor(imagesx($gdImage) * $this->factor), (int) floor(imagesy($gdImage) * $this->factor));
return $resize->apply($gdImage, $imageInfo);
}
}

View File

@ -17,9 +17,9 @@ class Sharpen extends AbstractTransform
return new static();
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_MEAN_REMOVAL);
return $image;
imagefilter($gdImage, IMG_FILTER_MEAN_REMOVAL);
return $gdImage;
}
}

View File

@ -17,9 +17,9 @@ class Smoothen extends AbstractTransform
return new static();
}
public function apply(GdImage $image, ImageInfo $info): GdImage
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage
{
imagefilter($image, IMG_FILTER_SMOOTH);
return $image;
imagefilter($gdImage, IMG_FILTER_SMOOTH);
return $gdImage;
}
}

View File

@ -8,7 +8,7 @@ use GdImage;
interface TransformInterface extends ArraySerializable
{
public function apply(GdImage $image, ImageInfo $info): GdImage;
public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage;
public function getSpecifier(): string;
}

View File

@ -11,7 +11,7 @@ class Interpolator
*/
public static function interpolate(string $string, array $vars): mixed
{
$interpolator = new NodeInterpolator(Parser::parseTokenStream(Tokenizer::tokenizeString($string)), $vars);
return $interpolator->interpolate();
$nodeInterpolator = new NodeInterpolator(Parser::parseTokenStream(Tokenizer::tokenizeString($string)), $vars);
return $nodeInterpolator->interpolate();
}
}

View File

@ -15,20 +15,11 @@ use InvalidArgumentException;
class NodeInterpolator
{
protected AbstractNode $node;
/**
* @var array<string, mixed>
*/
protected array $vars;
/**
* @param array<string, mixed> $vars
*/
public function __construct(AbstractNode $node, array $vars)
public function __construct(protected AbstractNode $node, protected array $vars)
{
$this->node = $node;
$this->vars = $vars;
}
/**
@ -63,16 +54,16 @@ class NodeInterpolator
*
* @param array<mixed>|object|null $parent
*/
protected function interpolateIdentifierNode(IdentifierNode $node, array|object|null $parent = null): mixed
protected function interpolateIdentifierNode(IdentifierNode $identifierNode, array|object|null $parent = null): mixed
{
$name = $node->value();
$name = $identifierNode->value();
$arguments = [];
$traverse = $node->traverse();
$traverse = $identifierNode->traverse();
if ($node->arguments() !== null) {
foreach ($node->arguments()->value() as $argument) {
if ($identifierNode->arguments() !== null) {
foreach ($identifierNode->arguments()->value() as $argument) {
$arguments[] = $this->interpolateNode($argument);
}
}
@ -82,40 +73,38 @@ class NodeInterpolator
throw new InterpolationException(sprintf('Undefined variable "%s"', $name));
}
$value = $this->vars[$name];
} else {
if (is_array($parent)) {
if (!array_key_exists($name, $parent)) {
throw new InterpolationException(sprintf('Undefined array key "%s"', $name));
}
$value = $parent[$name];
} elseif (is_object($parent)) {
switch (true) {
case method_exists($parent, $name):
$value = $parent->{$name}(...$arguments);
break;
case is_callable([$parent, '__call']):
$value = $parent->__call($name, $arguments);
break;
case property_exists($parent, $name) && $node->arguments() === null:
$value = $parent->{$name};
break;
case is_callable([$parent, '__get']) && $node->arguments() === null:
$value = $parent->__get($name);
break;
case defined($parent::class . '::' . $name) && $node->arguments() === null:
$value = constant($parent::class . '::' . $name);
break;
default:
throw new InterpolationException(sprintf('Undefined class method, property or constant %s::%s', $parent::class, $name));
}
} else {
throw new InvalidArgumentException(sprintf('%s() accepts only arrays and objects as $parent argument', __METHOD__));
} elseif (is_array($parent)) {
if (!array_key_exists($name, $parent)) {
throw new InterpolationException(sprintf('Undefined array key "%s"', $name));
}
$value = $parent[$name];
} elseif (is_object($parent)) {
switch (true) {
case method_exists($parent, $name):
$value = $parent->{$name}(...$arguments);
break;
case is_callable([$parent, '__call']):
$value = $parent->__call($name, $arguments);
break;
case property_exists($parent, $name) && $identifierNode->arguments() === null:
$value = $parent->{$name};
break;
case is_callable([$parent, '__get']) && $identifierNode->arguments() === null:
$value = $parent->__get($name);
break;
case defined($parent::class . '::' . $name) && $identifierNode->arguments() === null:
$value = constant($parent::class . '::' . $name);
break;
default:
throw new InterpolationException(sprintf('Undefined class method, property or constant %s::%s', $parent::class, $name));
}
} else {
throw new InvalidArgumentException(sprintf('%s() accepts only arrays and objects as $parent argument', __METHOD__));
}
if ($traverse !== null) {
@ -146,7 +135,7 @@ class NodeInterpolator
}
// Call closures if arguments (zero or more) are given
if ($value instanceof Closure && $node->arguments() !== null) {
if ($value instanceof Closure && $identifierNode->arguments() !== null) {
return $value(...$arguments);
}
@ -158,12 +147,12 @@ class NodeInterpolator
*
* @return array<mixed>
*/
protected function interpolateArrayNode(ArrayNode $node): array
protected function interpolateArrayNode(ArrayNode $arrayNode): array
{
$result = [];
$keys = $this->interpolateArrayKeysNode($node->keys());
$keys = $this->interpolateArrayKeysNode($arrayNode->keys());
foreach ($node->value() as $i => $value) {
foreach ($arrayNode->value() as $i => $value) {
$key = $keys[$i];
$result[$key] = $this->interpolateNode($value);
}
@ -176,13 +165,13 @@ class NodeInterpolator
*
* @return list<array-key>
*/
protected function interpolateArrayKeysNode(ArrayKeysNode $node): array
protected function interpolateArrayKeysNode(ArrayKeysNode $arrayKeysNode): array
{
$offset = -1;
$result = [];
foreach ($node->value() as $key) {
foreach ($arrayKeysNode->value() as $key) {
switch ($key->type()) {
case ImplicitArrayKeyNode::TYPE:
$offset++;
@ -222,14 +211,12 @@ class NodeInterpolator
protected function validateArrayKey(mixed $key): int|string
{
switch (true) {
case is_int($key):
return $key;
case is_bool($key):
case is_float($key):
case is_string($key) && ctype_digit($key) && $key[0] !== '0':
return (int) $key;
case is_int($key):
case is_string($key):
return $key;

View File

@ -2,7 +2,9 @@
namespace Formwork\Interpolator\Nodes;
abstract class AbstractNode
use Stringable;
abstract class AbstractNode implements Stringable
{
/**
* Node type
@ -14,7 +16,7 @@ abstract class AbstractNode
*/
protected mixed $value;
public function __toString()
public function __toString(): string
{
return 'node of type ' . static::TYPE;
}

View File

@ -9,15 +9,12 @@ class ArrayNode extends AbstractNode
*/
public const TYPE = 'array';
protected ArrayKeysNode $keys;
/**
* @param list<mixed> $value
*/
public function __construct(array $value, ArrayKeysNode $keys)
public function __construct(array $value, protected ArrayKeysNode $arrayKeysNode)
{
$this->value = $value;
$this->keys = $keys;
}
/**
@ -25,6 +22,6 @@ class ArrayNode extends AbstractNode
*/
public function keys(): ArrayKeysNode
{
return $this->keys;
return $this->arrayKeysNode;
}
}

View File

@ -9,21 +9,9 @@ class IdentifierNode extends AbstractNode
*/
public const TYPE = 'identifier';
/**
* Node arguments
*/
protected ?ArgumentsNode $arguments;
/**
* Node used to traverse
*/
protected ?AbstractNode $traverse;
public function __construct(string $value, ?ArgumentsNode $arguments, ?AbstractNode $traverse)
public function __construct(string $value, protected ?ArgumentsNode $argumentsNode, protected ?AbstractNode $node)
{
$this->value = $value;
$this->arguments = $arguments;
$this->traverse = $traverse;
}
/**
@ -31,7 +19,7 @@ class IdentifierNode extends AbstractNode
*/
public function arguments(): ?ArgumentsNode
{
return $this->arguments;
return $this->argumentsNode;
}
/**
@ -39,6 +27,6 @@ class IdentifierNode extends AbstractNode
*/
public function traverse(): ?AbstractNode
{
return $this->traverse;
return $this->node;
}
}

View File

@ -14,11 +14,8 @@ use Formwork\Interpolator\Nodes\StringNode;
class Parser implements ParserInterface
{
protected TokenStream $stream;
public function __construct(TokenStream $stream)
public function __construct(protected TokenStream $tokenStream)
{
$this->stream = $stream;
}
/**
@ -26,18 +23,18 @@ class Parser implements ParserInterface
*/
public function parse(): AbstractNode
{
$node = $this->parseIdentifierToken();
$this->stream->expectEnd();
return $node;
$identifierNode = $this->parseIdentifierToken();
$this->tokenStream->expectEnd();
return $identifierNode;
}
/**
* Parse a given TokenStream object
*/
public static function parseTokenStream(TokenStream $stream): AbstractNode
public static function parseTokenStream(TokenStream $tokenStream): AbstractNode
{
$parser = new static($stream);
return $parser->parse();
$static = new static($tokenStream);
return $static->parse();
}
/**
@ -45,21 +42,21 @@ class Parser implements ParserInterface
*/
protected function parseIdentifierToken(): IdentifierNode
{
$token = $this->stream->expect(Token::TYPE_IDENTIFIER);
$token = $this->tokenStream->expect(Token::TYPE_IDENTIFIER);
$traverse = null;
$arguments = null;
if ($this->stream->current()->test(Token::TYPE_PUNCTUATION, '(')) {
if ($this->tokenStream->current()->test(Token::TYPE_PUNCTUATION, '(')) {
$arguments = $this->parseArguments();
}
if ($this->stream->current()->test(Token::TYPE_PUNCTUATION, '.')) {
if ($this->tokenStream->current()->test(Token::TYPE_PUNCTUATION, '.')) {
$traverse = $this->parseDotNotation();
}
if ($this->stream->current()->test(Token::TYPE_PUNCTUATION, '[')) {
if ($this->tokenStream->current()->test(Token::TYPE_PUNCTUATION, '[')) {
$traverse = $this->parseBracketsNotation();
}
@ -72,7 +69,7 @@ class Parser implements ParserInterface
*/
protected function parseNumberToken(): NumberNode
{
$token = $this->stream->expect(Token::TYPE_NUMBER);
$token = $this->tokenStream->expect(Token::TYPE_NUMBER);
// @phpstan-ignore-next-line
return new NumberNode($token->value() + 0);
}
@ -82,7 +79,7 @@ class Parser implements ParserInterface
*/
protected function parseStringToken(): StringNode
{
$token = $this->stream->expect(Token::TYPE_STRING);
$token = $this->tokenStream->expect(Token::TYPE_STRING);
// @phpstan-ignore-next-line
return new StringNode(stripcslashes(trim($token->value(), '\'"')));
}
@ -92,7 +89,7 @@ class Parser implements ParserInterface
*/
protected function parseDotNotation(): IdentifierNode
{
$this->stream->expect(Token::TYPE_PUNCTUATION, '.');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, '.');
return $this->parseIdentifierToken();
}
@ -101,24 +98,17 @@ class Parser implements ParserInterface
*/
protected function parseBracketsNotation(): AbstractNode
{
$this->stream->expect(Token::TYPE_PUNCTUATION, '[');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, '[');
$token = $this->stream->current();
$token = $this->tokenStream->current();
switch ($token->type()) {
case Token::TYPE_NUMBER:
$key = $this->parseNumberToken();
break;
$key = match ($token->type()) {
Token::TYPE_NUMBER => $this->parseNumberToken(),
Token::TYPE_STRING => $this->parseStringToken(),
default => throw new SyntaxError(sprintf('Unexpected %s at position %d', $token, $token->position())),
};
case Token::TYPE_STRING:
$key = $this->parseStringToken();
break;
default:
throw new SyntaxError(sprintf('Unexpected %s at position %d', $token, $token->position()));
}
$this->stream->expect(Token::TYPE_PUNCTUATION, ']');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, ']');
return $key;
}
@ -128,18 +118,18 @@ class Parser implements ParserInterface
*/
protected function parseArguments(): ArgumentsNode
{
$this->stream->expect(Token::TYPE_PUNCTUATION, '(');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, '(');
$arguments = [];
while (!$this->stream->current()->test(Token::TYPE_PUNCTUATION, ')')) {
while (!$this->tokenStream->current()->test(Token::TYPE_PUNCTUATION, ')')) {
if ($arguments !== []) {
$this->stream->expect(Token::TYPE_PUNCTUATION, ',');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, ',');
}
$arguments[] = $this->parseExpression();
}
$this->stream->expect(Token::TYPE_PUNCTUATION, ')');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, ')');
return new ArgumentsNode($arguments);
}
@ -149,7 +139,7 @@ class Parser implements ParserInterface
*/
protected function parseExpression(): AbstractNode
{
$token = $this->stream->current();
$token = $this->tokenStream->current();
switch ($token->type()) {
case Token::TYPE_IDENTIFIER:
@ -177,21 +167,21 @@ class Parser implements ParserInterface
*/
protected function parseArrayExpression(): ArrayNode
{
$this->stream->expect(Token::TYPE_PUNCTUATION, '[');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, '[');
$elements = [];
$keys = [];
while (!$this->stream->current()->test(Token::TYPE_PUNCTUATION, ']')) {
while (!$this->tokenStream->current()->test(Token::TYPE_PUNCTUATION, ']')) {
if ($elements !== []) {
$this->stream->expect(Token::TYPE_PUNCTUATION, ',');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, ',');
}
$value = $this->parseExpression();
if ($this->stream->current()->test(Token::TYPE_ARROW)) {
$arrow = $this->stream->consume();
if ($this->tokenStream->current()->test(Token::TYPE_ARROW)) {
$arrow = $this->tokenStream->consume();
if ($value->type() === ArrayNode::TYPE) {
throw new SyntaxError(sprintf('Unexpected %s at position %d', $arrow, $arrow->position()));
@ -207,7 +197,7 @@ class Parser implements ParserInterface
$keys[] = $key;
}
$this->stream->expect(Token::TYPE_PUNCTUATION, ']');
$this->tokenStream->expect(Token::TYPE_PUNCTUATION, ']');
return new ArrayNode($elements, new ArrayKeysNode($keys));
}

View File

@ -6,7 +6,7 @@ use Formwork\Interpolator\Nodes\AbstractNode;
interface ParserInterface
{
public function __construct(TokenStream $stream);
public function __construct(TokenStream $tokenStream);
/**
* Parse the tokens
@ -16,5 +16,5 @@ interface ParserInterface
/**
* Parse a given TokenStream object
*/
public static function parseTokenStream(TokenStream $stream): AbstractNode;
public static function parseTokenStream(TokenStream $tokenStream): AbstractNode;
}

View File

@ -2,7 +2,9 @@
namespace Formwork\Interpolator;
class Token
use Stringable;
class Token implements Stringable
{
/**
* Identifier token type
@ -34,29 +36,11 @@ class Token
*/
public const TYPE_END = 'end';
/**
* Token type
*/
protected string $type;
/**
* Token value
*/
protected ?string $value;
/**
* Token position
*/
protected int $position;
public function __construct(string $type, ?string $value, int $position)
public function __construct(protected string $type, protected ?string $value, protected int $position)
{
$this->type = $type;
$this->value = $value;
$this->position = $position;
}
public function __toString()
public function __toString(): string
{
return sprintf(
'token%s of type %s',

View File

@ -6,13 +6,6 @@ use Formwork\Interpolator\Errors\SyntaxError;
class TokenStream
{
/**
* Array containing tokens
*
* @var list<Token>
*/
protected array $tokens;
/**
* Pointer to the current token
*/
@ -26,9 +19,8 @@ class TokenStream
/**
* @param list<Token> $tokens
*/
public function __construct(array $tokens)
public function __construct(protected array $tokens)
{
$this->tokens = $tokens;
$this->count = count($tokens);
}

View File

@ -36,11 +36,6 @@ class Tokenizer implements TokenizerInterface
*/
protected const ARROW_SEQUENCE = '=>';
/**
* Tokenizer input string
*/
protected string $input;
/**
* Tokenizer input length
*/
@ -51,9 +46,8 @@ class Tokenizer implements TokenizerInterface
*/
protected int $position = 0;
public function __construct(string $input)
public function __construct(protected string $input)
{
$this->input = $input;
$this->length = strlen($input);
}
@ -114,7 +108,7 @@ class Tokenizer implements TokenizerInterface
*/
public static function tokenizeString(string $string): TokenStream
{
$tokenizer = new static($string);
return $tokenizer->tokenize();
$static = new static($string);
return $static->tokenize();
}
}

View File

@ -2,13 +2,10 @@
namespace Formwork\Languages;
class Language
{
/**
* Language code
*/
protected string $code;
use Stringable;
class Language implements Stringable
{
/**
* Language name (in English)
*/
@ -19,10 +16,8 @@ class Language
*/
protected ?string $nativeName = null;
public function __construct(string $code)
public function __construct(protected string $code)
{
$this->code = $code;
if (LanguageCodes::hasCode($code)) {
$this->name = LanguageCodes::codeToName($code);
$this->nativeName = LanguageCodes::codeToNativeName($code);

View File

@ -100,15 +100,12 @@ class Languages
*/
protected function resolveLanguage(Language|string|null $language): ?Language
{
switch (true) {
case $language instanceof Language:
return $language;
case is_string($language):
return $this->available->get($language, null);
default:
return null;
if ($language instanceof Language) {
return $language;
}
if (is_string($language)) {
return $this->available->get($language, null);
}
return null;
}
}

View File

@ -4,18 +4,12 @@ namespace Formwork\Log;
class Log extends Registry
{
/**
* Limit of registry entries
*/
protected int $limit;
/**
* Create a new Log instance
*/
public function __construct(string $filename, int $limit = 128)
public function __construct(string $filename, protected int $limit = 128)
{
parent::__construct($filename);
$this->limit = $limit;
}
/**

View File

@ -15,11 +15,6 @@ class Registry
*/
protected array $storage = [];
/**
* Registry filename
*/
protected string $filename;
/**
* Whether the registry is saved
*/
@ -28,9 +23,8 @@ class Registry
/**
* Create a new Registry instance
*/
public function __construct(string $filename)
public function __construct(protected string $filename)
{
$this->filename = $filename;
if (FileSystem::exists($this->filename)) {
$this->storage = Json::parseFile($filename);
$this->saved = true;

View File

@ -3,8 +3,9 @@
namespace Formwork\Metadata;
use Formwork\Utils\Str;
use Stringable;
class Metadata
class Metadata implements Stringable
{
protected const HTTP_EQUIV_NAMES = ['content-type', 'default-style', 'refresh'];
@ -13,11 +14,6 @@ class Metadata
*/
protected string $name;
/**
* Metadata content
*/
protected string $content;
/**
* Metadata prefix
*/
@ -26,14 +22,14 @@ class Metadata
/**
* Create a new Metadata instance
*/
public function __construct(string $name, string $content)
public function __construct(string $name, protected string $content)
{
$this->name = strtolower($name);
$this->content = $content;
if ($prefix = Str::before($name, ':')) {
$this->prefix = $prefix;
if (($prefix = Str::before($name, ':')) === '') {
return;
}
$this->prefix = $prefix;
}
public function __toString(): string

View File

@ -26,9 +26,10 @@ use Formwork\Utils\Uri;
use InvalidArgumentException;
use ReflectionClass;
use RuntimeException;
use Stringable;
use UnexpectedValueException;
class Page implements Arrayable
class Page implements Arrayable, Stringable
{
use PageData;
use PageStatus;
@ -147,7 +148,7 @@ class Page implements Arrayable
$this->loadFiles();
if ($this->contentFile && !$this->contentFile->isEmpty()) {
if ($this->contentFile instanceof ContentFile && !$this->contentFile->isEmpty()) {
$this->data = [
...$this->data,
...$this->contentFile->frontmatter(),
@ -162,7 +163,7 @@ class Page implements Arrayable
public function __toString(): string
{
return $this->title() ?? $this->slug();
return (string) ($this->title() ?? $this->slug());
}
public function site(): Site
@ -250,13 +251,9 @@ class Page implements Arrayable
*/
public function canonicalRoute(): ?string
{
if (isset($this->canonicalRoute)) {
return $this->canonicalRoute;
}
return $this->canonicalRoute = !empty($this->data['canonicalRoute'])
? Path::normalize($this->data['canonicalRoute'])
: null;
return $this->canonicalRoute ?? ($this->canonicalRoute = empty($this->data['canonicalRoute'])
? null
: Path::normalize($this->data['canonicalRoute']));
}
/**
@ -272,7 +269,7 @@ class Page implements Arrayable
*/
public function num(): ?int
{
if (isset($this->num)) {
if ($this->num !== null) {
return $this->num;
}
@ -356,7 +353,7 @@ class Page implements Arrayable
// Get a default 404 Not Found status for the error page
if ($this->isErrorPage() && $this->responseStatus() === ResponseStatus::OK
&& !isset($this->contentFile, $this->contentFile->frontmatter()['responseStatus'])) {
&& $this->contentFile === null) {
$this->responseStatus = ResponseStatus::NotFound;
}
@ -522,7 +519,7 @@ class Page implements Arrayable
$site = $this->site;
if (isset($this->path) && FileSystem::isDirectory($this->path, assertExists: false)) {
if ($this->path !== null && FileSystem::isDirectory($this->path, assertExists: false)) {
foreach (FileSystem::listFiles($this->path) as $file) {
$name = FileSystem::name($file);
@ -567,7 +564,7 @@ class Page implements Arrayable
: array_keys($contentFiles)[0];
// Set actual language
$this->language ??= $key ? new Language($key) : null;
$this->language ??= $key !== '' ? new Language($key) : null;
$this->contentFile ??= new ContentFile($contentFiles[$key]['path']);
@ -627,11 +624,11 @@ class Page implements Arrayable
{
$reflectionClass = new ReflectionClass($this);
foreach ($reflectionClass->getProperties() as $property) {
unset($this->{$property->getName()});
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
unset($this->{$reflectionProperty->getName()});
if ($property->hasDefaultValue()) {
$this->{$property->getName()} = $property->getDefaultValue();
if ($reflectionProperty->hasDefaultValue()) {
$this->{$reflectionProperty->getName()} = $reflectionProperty->getDefaultValue();
}
}
}

View File

@ -98,7 +98,7 @@ class PageCollection extends AbstractCollection implements Paginable
$value = Str::removeHTML((string) $page->get($key));
$queryMatches = preg_match_all($queryRegex, $value);
$keywordsMatches = empty($keywords) ? 0 : preg_match_all($keywordsRegex, $value);
$keywordsMatches = $keywords === [] ? 0 : preg_match_all($keywordsRegex, $value);
$score += ($queryMatches * 2 + min($keywordsMatches, 3)) * $scores[$key];
}

View File

@ -9,8 +9,8 @@ class Pagination extends BasePagination
{
use PaginationUri;
public function __construct(PageCollection $collection, int $length)
public function __construct(PageCollection $pageCollection, int $length)
{
parent::__construct($collection, $length);
parent::__construct($pageCollection, $length);
}
}

View File

@ -20,8 +20,9 @@ use Formwork\Schemes\Scheme;
use Formwork\Schemes\Schemes;
use Formwork\Utils\Arr;
use Formwork\Utils\FileSystem;
use Stringable;
class Site implements Arrayable
class Site implements Arrayable, Stringable
{
use PageData;
use PageTraversal;
@ -116,9 +117,9 @@ class Site implements Arrayable
$this->setMultiple($data);
}
public function __toString()
public function __toString(): string
{
return $this->title();
return (string) $this->title();
}
public function site(): Site
@ -308,10 +309,7 @@ class Site implements Arrayable
*/
public function retrievePage(string $path): Page
{
if (isset($this->storage[$path])) {
return $this->storage[$path];
}
return $this->storage[$path] = new Page(['site' => $this, 'path' => $path]);
return $this->storage[$path] ?? ($this->storage[$path] = new Page(['site' => $this, 'path' => $path]));
}
public function retrievePages(string $path, bool $recursive = false): PageCollection
@ -372,9 +370,7 @@ class Site implements Arrayable
}
}
$page = $this->retrievePage($path);
return $page;
return $this->retrievePage($path);
}
/**

View File

@ -11,8 +11,9 @@ use Formwork\Utils\FileSystem;
use Formwork\View\Exceptions\RenderingException;
use Formwork\View\Renderer;
use Formwork\View\View;
use Stringable;
class Template extends View
class Template extends View implements Stringable
{
/**
* @inheritdoc
@ -47,13 +48,10 @@ class Template extends View
*/
public function assets(): Assets
{
if (isset($this->assets)) {
return $this->assets;
}
return $this->assets = new Assets(
return $this->assets ?? ($this->assets = new Assets(
$this->path() . 'assets/',
$this->site->uri('/site/templates/assets/', includeLanguage: false)
);
));
}
/**

View File

@ -30,9 +30,13 @@ trait PageData
*/
public function has(string $key): bool
{
return (property_exists($this, $key) && !(new ReflectionProperty($this, $key))->isPromoted())
|| $this->fields->has($key)
|| Arr::has($this->data, $key);
if (property_exists($this, $key) && !(new ReflectionProperty($this, $key))->isPromoted()) {
return true;
}
if ($this->fields->has($key)) {
return true;
}
return Arr::has($this->data, $key);
}
/**

View File

@ -201,11 +201,7 @@ trait PageTraversal
*/
public function siblings(): PageCollection
{
if (isset($this->siblings)) {
return $this->siblings;
}
return $this->siblings = $this->inclusiveSiblings()->without($this);
return $this->siblings ?? ($this->siblings = $this->inclusiveSiblings()->without($this));
}
/**

View File

@ -133,8 +133,8 @@ trait PaginationUri
$router = App::instance()->router();
if ($router->current() === null) {
throw new RuntimeException(sprintf('Cannot generate pagination routes, current route is not defined'));
if (!$router->current() instanceof Route) {
throw new RuntimeException('Cannot generate pagination routes, current route is not defined');
}
$routeName = Str::removeEnd($router->current()->getName(), static::$routeSuffix);

View File

@ -69,9 +69,9 @@ abstract class AbstractController extends BaseAbstractController
return $this->router->generate($name, $params);
}
protected function redirect(string $route, ResponseStatus $status = ResponseStatus::Found): RedirectResponse
protected function redirect(string $route, ResponseStatus $responseStatus = ResponseStatus::Found): RedirectResponse
{
return new RedirectResponse($this->site->uri($route, includeLanguage: false), $status);
return new RedirectResponse($this->site->uri($route, includeLanguage: false), $responseStatus);
}
/**
@ -79,12 +79,12 @@ abstract class AbstractController extends BaseAbstractController
*
* @param string $default Default route if HTTP referer is not available
*/
protected function redirectToReferer(ResponseStatus $status = ResponseStatus::Found, string $default = '/'): RedirectResponse
protected function redirectToReferer(ResponseStatus $responseStatus = ResponseStatus::Found, string $default = '/'): RedirectResponse
{
if (!in_array($this->request->referer(), [null, Uri::current()], true) && $this->request->validateReferer($this->panel()->uri('/'))) {
return new RedirectResponse($this->request->referer(), $status);
return new RedirectResponse($this->request->referer(), $responseStatus);
}
return new RedirectResponse($this->panel()->uri($default), $status);
return new RedirectResponse($this->panel()->uri($default), $responseStatus);
}
protected function translate(string $key, int|float|string|Stringable ...$arguments): string

View File

@ -18,9 +18,9 @@ class AuthenticationController extends AbstractController
/**
* Authentication@login action
*/
public function login(Request $request, CsrfToken $csrfToken, AccessLimiter $limiter): Response
public function login(Request $request, CsrfToken $csrfToken, AccessLimiter $accessLimiter): Response
{
if ($limiter->hasReachedLimit()) {
if ($accessLimiter->hasReachedLimit()) {
$minutes = round($this->config->get('system.panel.loginResetTime') / 60);
$csrfToken->generate();
return $this->error($this->translate('panel.login.attempt.tooMany', $minutes));
@ -51,7 +51,7 @@ class AuthenticationController extends AbstractController
$this->error($this->translate('panel.login.attempt.failed'));
}
$limiter->registerAttempt();
$accessLimiter->registerAttempt();
$user = $this->panel()->users()->get($data->get('username'));
@ -69,7 +69,7 @@ class AuthenticationController extends AbstractController
$time = $accessLog->log($data->get('username'));
$lastAccessRegistry->set($data->get('username'), $time);
$limiter->resetAttempts();
$accessLimiter->resetAttempts();
if (($destination = $request->session()->get('FORMWORK_REDIRECT_TO')) !== null) {
$request->session()->remove('FORMWORK_REDIRECT_TO');

View File

@ -36,10 +36,10 @@ class BackupController extends AbstractController
/**
* Backup@download action
*/
public function download(RouteParams $params): Response
public function download(RouteParams $routeParams): Response
{
$this->ensurePermission('backup.download');
$file = FileSystem::joinPaths($this->config->get('system.backup.path'), basename(base64_decode($params->get('backup'))));
$file = FileSystem::joinPaths($this->config->get('system.backup.path'), basename(base64_decode($routeParams->get('backup'))));
try {
if (FileSystem::isFile($file, assertExists: false)) {
return new FileResponse($file, download: true);

View File

@ -23,10 +23,10 @@ class ErrorsController extends AbstractController
/**
* Errors@internalServerError action
*/
public function internalServerError(Throwable $exception): Response
public function internalServerError(Throwable $throwable): Response
{
return $this->makeErrorResponse(ResponseStatus::InternalServerError, 'internalServerError', [
'href' => $this->makeGitHubIssueUri($exception),
'href' => $this->makeGitHubIssueUri($throwable),
'label' => $this->translate('panel.errors.action.reportToGithub'),
]);
}
@ -47,32 +47,32 @@ class ErrorsController extends AbstractController
*
* @param array<mixed> $action
*/
protected function makeErrorResponse(ResponseStatus $status, string $name, array $action): Response
protected function makeErrorResponse(ResponseStatus $responseStatus, string $name, array $action): Response
{
Response::cleanOutputBuffers();
if ($this->request->isXmlHttpRequest()) {
return JsonResponse::error('Error', $status);
return JsonResponse::error('Error', $responseStatus);
}
return new Response($this->view('errors.error', [
'title' => $this->translate('panel.errors.error.' . $name . '.status'),
'code' => $status->code(),
'code' => $responseStatus->code(),
'status' => $this->translate('panel.errors.error.' . $name . '.status'),
'heading' => $this->translate('panel.errors.error.' . $name . '.heading'),
'description' => $this->translate('panel.errors.error.' . $name . '.description'),
'action' => $action,
]), $status);
]), $responseStatus);
}
/**
* Make a URI to a new GitHub issue with pre-filled data from an (uncaught) exception
*/
protected function makeGitHubIssueUri(Throwable $exception): string
protected function makeGitHubIssueUri(Throwable $throwable): string
{
$query = http_build_query([
'labels' => 'bug',
'title' => $exception->getMessage(),
'title' => $throwable->getMessage(),
'body' => sprintf(
"### Description\n\n[Please enter a description and the steps to reproduce the problem...]\n\n" .
"**Formwork**: %s\n**Php**: %s\n**OS**: %s\n**SAPI**: %s\n\n" .
@ -81,11 +81,11 @@ class ErrorsController extends AbstractController
PHP_VERSION,
PHP_OS_FAMILY,
PHP_SAPI,
$exception::class,
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
$throwable::class,
$throwable->getMessage(),
$throwable->getFile(),
$throwable->getLine(),
$throwable->getTraceAsString()
),
]);

View File

@ -265,13 +265,13 @@ class OptionsController extends AbstractController
* @param array<string, mixed> $options
* @param array<string, mixed> $defaults
*/
protected function updateOptions(string $type, FieldCollection $fields, array $options, array $defaults): bool
protected function updateOptions(string $type, FieldCollection $fieldCollection, array $options, array $defaults): bool
{
$old = $options;
$options = [];
// Update options with new values
foreach ($fields as $field) {
foreach ($fieldCollection as $field) {
if ($field->isRequired() && $field->isEmpty()) {
continue;
}

View File

@ -13,6 +13,7 @@ use Formwork\Http\RequestData;
use Formwork\Http\RequestMethod;
use Formwork\Http\Response;
use Formwork\Images\Image;
use Formwork\Languages\Language;
use Formwork\Pages\Page;
use Formwork\Pages\Site;
use Formwork\Parsers\Yaml;
@ -81,11 +82,11 @@ class PagesController extends AbstractController
{
$this->ensurePermission('pages.create');
$data = $this->request->input();
$requestData = $this->request->input();
// Let's create the page
try {
$page = $this->createPage($data);
$page = $this->createPage($requestData);
$this->panel()->notify($this->translate('panel.pages.page.created'), 'success');
} catch (TranslatedException $e) {
$this->panel()->notify($e->getTranslatedMessage(), 'error');
@ -102,18 +103,18 @@ class PagesController extends AbstractController
/**
* Pages@edit action
*/
public function edit(RouteParams $params): Response
public function edit(RouteParams $routeParams): Response
{
$this->ensurePermission('pages.edit');
$page = $this->site()->findPage($params->get('page'));
$page = $this->site()->findPage($routeParams->get('page'));
if ($page === null) {
$this->panel()->notify($this->translate('panel.pages.page.cannotEdit.pageNotFound'), 'error');
return $this->redirectToReferer(default: '/pages/');
}
if ($params->has('language')) {
if ($routeParams->has('language')) {
if (empty($this->config->get('system.languages.available'))) {
if ($page->route() === null) {
throw new UnexpectedValueException('Unexpected missing page route');
@ -121,7 +122,7 @@ class PagesController extends AbstractController
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => trim($page->route(), '/')]));
}
$language = $params->get('language');
$language = $routeParams->get('language');
if (!in_array($language, $this->config->get('system.languages.available'), true)) {
$this->panel()->notify($this->translate('panel.pages.page.cannotEdit.invalidLanguage', $language), 'error');
@ -186,7 +187,7 @@ class PagesController extends AbstractController
}
// Redirect if page route has changed
if ($params->get('page') !== ($route = trim($page->route(), '/'))) {
if ($routeParams->get('page') !== ($route = trim($page->route(), '/'))) {
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $route]));
}
@ -213,7 +214,7 @@ class PagesController extends AbstractController
'fields' => $fields,
'templates' => $this->site()->templates()->keys(),
'parents' => $this->site()->descendants()->sortBy('relativePath'),
'currentLanguage' => $params->get('language', $page->language()?->code()),
'currentLanguage' => $routeParams->get('language', $page->language()?->code()),
]));
}
@ -224,30 +225,30 @@ class PagesController extends AbstractController
{
$this->ensurePermission('pages.reorder');
$data = $this->request->input();
$requestData = $this->request->input();
if (!$data->hasMultiple(['page', 'before', 'parent'])) {
if (!$requestData->hasMultiple(['page', 'before', 'parent'])) {
return JsonResponse::error($this->translate('panel.pages.page.cannotMove'));
}
$parent = $this->resolveParent($data->get('parent'));
$parent = $this->resolveParent($requestData->get('parent'));
if (!$parent->hasChildren()) {
return JsonResponse::error($this->translate('panel.pages.page.cannotMove'));
}
$pages = $parent->children();
$keys = $pages->keys();
$pageCollection = $parent->children();
$keys = $pageCollection->keys();
$from = Arr::indexOf($keys, $data->get('page'));
$to = Arr::indexOf($keys, $data->get('before'));
$from = Arr::indexOf($keys, $requestData->get('page'));
$to = Arr::indexOf($keys, $requestData->get('before'));
if ($from === null || $to === null) {
return JsonResponse::error($this->translate('panel.pages.page.cannotMove'));
}
$pages->moveItem($from, $to);
$pageCollection->moveItem($from, $to);
foreach ($pages->values() as $i => $page) {
foreach ($pageCollection->values() as $i => $page) {
$name = basename($page->relativePath());
$newName = preg_replace(Page::NUM_REGEX, $i + 1 . '-', $name)
?? throw new RuntimeException(sprintf('Replacement failed with error: %s', preg_last_error_msg()));
@ -263,19 +264,19 @@ class PagesController extends AbstractController
/**
* Pages@delete action
*/
public function delete(RouteParams $params): RedirectResponse
public function delete(RouteParams $routeParams): RedirectResponse
{
$this->ensurePermission('pages.delete');
$page = $this->site()->findPage($params->get('page'));
$page = $this->site()->findPage($routeParams->get('page'));
if ($page === null) {
$this->panel()->notify($this->translate('panel.pages.page.cannotDelete.pageNotFound'), 'error');
return $this->redirectToReferer(default: '/pages/');
}
if ($params->has('language')) {
$language = $params->get('language');
if ($routeParams->has('language')) {
$language = $routeParams->get('language');
if ($page->languages()->available()->has($language)) {
$page->setLanguage($language);
} else {
@ -291,7 +292,7 @@ class PagesController extends AbstractController
if ($page->path() !== null) {
// Delete just the content file only if there are more than one language
if ($page->contentFile() && $params->has('language') && count($page->languages()->available()) > 1) {
if ($page->contentFile() !== null && $routeParams->has('language') && count($page->languages()->available()) > 1) {
FileSystem::delete($page->contentFile()->path());
} else {
FileSystem::delete($page->path(), recursive: true);
@ -301,7 +302,7 @@ class PagesController extends AbstractController
$this->panel()->notify($this->translate('panel.pages.page.deleted'), 'success');
// Try to redirect to referer unless it's to Pages@edit
if ($this->request->referer() !== null && !Str::startsWith(Uri::normalize($this->request->referer()), Uri::make(['path' => $this->panel()->uri('/pages/' . $params->get('page') . '/edit/')]))) {
if ($this->request->referer() !== null && !Str::startsWith(Uri::normalize($this->request->referer()), Uri::make(['path' => $this->panel()->uri('/pages/' . $routeParams->get('page') . '/edit/')]))) {
return $this->redirectToReferer(default: '/pages/');
}
return $this->redirect($this->generateRoute('panel.pages'));
@ -310,11 +311,11 @@ class PagesController extends AbstractController
/**
* Pages@uploadFile action
*/
public function uploadFile(RouteParams $params): RedirectResponse
public function uploadFile(RouteParams $routeParams): RedirectResponse
{
$this->ensurePermission('pages.uploadFiles');
$page = $this->site()->findPage($params->get('page'));
$page = $this->site()->findPage($routeParams->get('page'));
if ($page === null) {
$this->panel()->notify($this->translate('panel.pages.page.cannotUploadFile.pageNotFound'), 'error');
@ -326,146 +327,146 @@ class PagesController extends AbstractController
$this->processPageUploads($this->request->files()->getAll(), $page);
} catch (TranslatedException $e) {
$this->panel()->notify($this->translate('panel.uploader.error', $e->getTranslatedMessage()), 'error');
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
}
$this->panel()->notify($this->translate('panel.uploader.uploaded'), 'success');
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
/**
* Pages@deleteFile action
*/
public function deleteFile(RouteParams $params): RedirectResponse
public function deleteFile(RouteParams $routeParams): RedirectResponse
{
$this->ensurePermission('pages.deleteFiles');
$page = $this->site()->findPage($params->get('page'));
$page = $this->site()->findPage($routeParams->get('page'));
if ($page === null) {
$this->panel()->notify($this->translate('panel.pages.page.cannotDeleteFile.pageNotFound'), 'error');
return $this->redirectToReferer(default: '/pages/');
}
if (!$page->files()->has($params->get('filename'))) {
if (!$page->files()->has($routeParams->get('filename'))) {
$this->panel()->notify($this->translate('panel.pages.page.cannotDeleteFile.fileNotFound'), 'error');
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
FileSystem::delete($page->path() . $params->get('filename'));
FileSystem::delete($page->path() . $routeParams->get('filename'));
$this->panel()->notify($this->translate('panel.pages.page.fileDeleted'), 'success');
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
/**
* Pages@renameFile action
*/
public function renameFile(RouteParams $params, Request $request): RedirectResponse
public function renameFile(RouteParams $routeParams, Request $request): RedirectResponse
{
$this->ensurePermission('pages.renameFiles');
$page = $this->site()->findPage($params->get('page'));
$page = $this->site()->findPage($routeParams->get('page'));
if ($page === null) {
$this->panel()->notify($this->translate('panel.pages.page.cannotRenameFile.pageNotFound'), 'error');
return $this->redirectToReferer(default: '/pages/');
}
if (!$page->files()->has($params->get('filename'))) {
if (!$page->files()->has($routeParams->get('filename'))) {
$this->panel()->notify($this->translate('panel.pages.page.cannotRenameFile.fileNotFound'), 'error');
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
$name = Str::slug(FileSystem::name($request->input()->get('filename')));
$extension = FileSystem::extension($params->get('filename'));
$extension = FileSystem::extension($routeParams->get('filename'));
$newName = $name . '.' . $extension;
$previousName = $params->get('filename');
$previousName = $routeParams->get('filename');
if ($newName !== $previousName) {
if ($page->files()->has($newName)) {
$this->panel()->notify($this->translate('panel.pages.page.cannotRenameFile.fileAlreadyExists'), 'error');
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
FileSystem::move($page->path() . $previousName, $page->path() . $newName);
$this->panel()->notify($this->translate('panel.pages.page.fileRenamed'), 'success');
}
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $params->get('page')]));
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
}
public function getFileInfo(RouteParams $params): JsonResponse
public function getFileInfo(RouteParams $routeParams): JsonResponse
{
$this->ensurePermission('pages.getFileInfo');
$page = $this->site()->findPage($params->get('page'));
$page = $this->site()->findPage($routeParams->get('page'));
if ($page === null) {
return JsonResponse::error($this->translate('panel.pages.page.cannotRenameFile.pageNotFound'));
}
if (!$page->files()->has($params->get('filename'))) {
if (!$page->files()->has($routeParams->get('filename'))) {
return JsonResponse::error($this->translate('panel.pages.page.cannotRenameFile.fileNotFound'));
}
return JsonResponse::success('Yes!', data: $page->files()->get($params->get('filename'))->toArray());
return JsonResponse::success('Yes!', data: $page->files()->get($routeParams->get('filename'))->toArray());
}
/**
* Create a new page
*/
protected function createPage(RequestData $data): Page
protected function createPage(RequestData $requestData): Page
{
// Ensure no required data is missing
if (!$data->hasMultiple(['title', 'slug', 'template', 'parent'])) {
if (!$requestData->hasMultiple(['title', 'slug', 'template', 'parent'])) {
throw new TranslatedException('Missing required POST data', 'panel.pages.page.cannotCreate.varMissing');
}
try {
$parent = $this->resolveParent($data->get('parent'));
$parent = $this->resolveParent($requestData->get('parent'));
} catch (RuntimeException) {
throw new TranslatedException('Parent page not found', 'panel.pages.page.cannotCreate.invalidParent');
}
// Validate page slug
if (!$this->validateSlug($data->get('slug'))) {
if (!$this->validateSlug($requestData->get('slug'))) {
throw new TranslatedException('Invalid page slug', 'panel.pages.page.cannotCreate.invalidSlug');
}
$route = $parent->route() . $data->get('slug') . '/';
$route = $parent->route() . $requestData->get('slug') . '/';
// Ensure there isn't a page with the same route
if ($this->site()->findPage($route)) {
if ($this->site()->findPage($route) !== null) {
throw new TranslatedException('A page with the same route already exists', 'panel.pages.page.cannotCreate.alreadyExists');
}
// Validate page template
if (!$this->site()->templates()->has($data->get('template'))) {
if (!$this->site()->templates()->has($requestData->get('template'))) {
throw new TranslatedException('Invalid page template', 'panel.pages.page.cannotCreate.invalidTemplate');
}
$scheme = $this->app->schemes()->get('pages.' . $data->get('template'));
$scheme = $this->app->schemes()->get('pages.' . $requestData->get('template'));
$path = $parent->path() . $this->makePageNum($parent, $scheme->options()->get('num')) . '-' . $data->get('slug') . '/';
$path = $parent->path() . $this->makePageNum($parent, $scheme->options()->get('num')) . '-' . $requestData->get('slug') . '/';
FileSystem::createDirectory($path, recursive: true);
$language = $this->site()->languages()->default();
$filename = $data->get('template');
$filename .= empty($language) ? '' : '.' . $language;
$filename = $requestData->get('template');
$filename .= $language !== null ? '.' . $language : '';
$filename .= $this->config->get('system.content.extension');
FileSystem::createFile($path . $filename);
$contentData = [
'title' => $data->get('title'),
'title' => $requestData->get('title'),
'published' => false,
];
@ -479,14 +480,14 @@ class PagesController extends AbstractController
/**
* Update a page
*/
protected function updatePage(Page $page, RequestData $data, FieldCollection $fields): Page
protected function updatePage(Page $page, RequestData $requestData, FieldCollection $fieldCollection): Page
{
// Ensure no required data is missing
if (!$data->hasMultiple(['title', 'content'])) {
if (!$requestData->hasMultiple(['title', 'content'])) {
throw new TranslatedException('Missing required POST data', 'panel.pages.page.cannotEdit.varMissing');
}
if (!$page->contentFile()) {
if ($page->contentFile() === null) {
throw new RuntimeException('Unexpected missing content file');
}
@ -494,15 +495,15 @@ class PagesController extends AbstractController
$frontmatter = $page->contentFile()->frontmatter();
// Preserve the title if not given
if (!empty($data->get('title'))) {
$frontmatter['title'] = $data->get('title');
if (!empty($requestData->get('title'))) {
$frontmatter['title'] = $requestData->get('title');
}
// Get page defaults
$defaults = $page->defaults();
// Handle data from fields
foreach ($fields as $field) {
foreach ($fieldCollection as $field) {
$default = array_key_exists($field->name(), $defaults) && $field->value() === $defaults[$field->name()];
// Remove empty and default values
@ -515,9 +516,9 @@ class PagesController extends AbstractController
$frontmatter[$field->name()] = $field->value();
}
$content = str_replace("\r\n", "\n", $data->get('content'));
$content = str_replace("\r\n", "\n", $requestData->get('content'));
$language = $data->get('language');
$language = $requestData->get('language');
// Validate language
if (!empty($language) && !in_array($language, $this->config->get('system.languages.available'), true)) {
@ -527,7 +528,7 @@ class PagesController extends AbstractController
$differ = $frontmatter !== $page->contentFile()->frontmatter() || $content !== $page->data()['content'] || $language !== $page->language();
if ($differ) {
$filename = $data->get('template');
$filename = $requestData->get('template');
$filename .= empty($language) ? '' : '.' . $language;
$filename .= $this->config->get('system.content.extension');
@ -552,7 +553,7 @@ class PagesController extends AbstractController
$page->setLanguage($language);
}
if (!$page->contentFile()) {
if ($page->contentFile() === null) {
throw new RuntimeException('Unexpected missing content file');
}
@ -572,7 +573,7 @@ class PagesController extends AbstractController
try {
$page = $this->changePageName($page, $name);
} catch (RuntimeException $e) {
} catch (RuntimeException) {
throw new TranslatedException('Cannot change page num', 'panel.pages.page.cannotChangeNum');
}
}
@ -580,7 +581,7 @@ class PagesController extends AbstractController
// Check if parent page has to change
try {
if ($page->parent() !== ($parent = $this->resolveParent($data->get('parent')))) {
if ($page->parent() !== ($parent = $this->resolveParent($requestData->get('parent')))) {
$page = $this->changePageParent($page, $parent);
}
} catch (RuntimeException) {
@ -588,7 +589,7 @@ class PagesController extends AbstractController
}
// Check if page template has to change
if ($page->template()->name() !== ($template = $data->get('template'))) {
if ($page->template()->name() !== ($template = $requestData->get('template'))) {
if (!$this->site()->templates()->has($template)) {
throw new TranslatedException('Invalid page template', 'panel.pages.page.cannotEdit.invalidTemplate');
}
@ -596,7 +597,7 @@ class PagesController extends AbstractController
}
// Check if page slug has to change
if ($page->slug() !== ($slug = $data->get('slug'))) {
if ($page->slug() !== ($slug = $requestData->get('slug'))) {
if (!$this->validateSlug($slug)) {
throw new TranslatedException('Invalid page slug', 'panel.pages.page.cannotEdit.invalidSlug');
}
@ -604,7 +605,7 @@ class PagesController extends AbstractController
if ($page->isIndexPage() || $page->isErrorPage()) {
throw new TranslatedException('Cannot change slug of index or error pages', 'panel.pages.page.cannotEdit.indexOrErrorPageSlug');
}
if ($this->site()->findPage($page->parent()?->route() . $slug . '/')) {
if ($this->site()->findPage($page->parent()?->route() . $slug . '/') !== null) {
throw new TranslatedException('A page with the same route already exists', 'panel.pages.page.cannotEdit.alreadyExists');
}
$page = $this->changePageName($page, ltrim($page->num() . '-', '-') . $slug);
@ -620,7 +621,7 @@ class PagesController extends AbstractController
*/
protected function processPageUploads(array $files, Page $page): void
{
$uploader = new FileUploader($this->config);
$fileUploader = new FileUploader($this->config);
foreach ($files as $file) {
if (!$file->isUploaded()) {
@ -629,7 +630,7 @@ class PagesController extends AbstractController
if ($page->path() === null) {
throw new UnexpectedValueException('Unexpected missing page path');
}
$uploadedFile = $uploader->upload($file, $page->path());
$uploadedFile = $fileUploader->upload($file, $page->path());
// Process JPEG and PNG images according to system options (e.g. quality)
if ($this->config->get('system.uploads.processImages') && in_array($uploadedFile->mimeType(), ['image/jpeg', 'image/png'], true)) {
$image = new Image($uploadedFile->path(), $this->config->get('system.images'));
@ -697,7 +698,7 @@ class PagesController extends AbstractController
throw new UnexpectedValueException('Unexpected missing page path');
}
if (!$page->contentFile()) {
if ($page->contentFile() === null) {
throw new UnexpectedValueException('Unexpected missing content file');
}

View File

@ -20,7 +20,7 @@ class UpdatesController extends AbstractController
$this->ensurePermission('updates.check');
try {
$upToDate = $updater->checkUpdates();
} catch (RuntimeException $e) {
} catch (RuntimeException) {
return JsonResponse::error($this->translate('panel.updates.status.cannotCheck'), ResponseStatus::InternalServerError, [
'status' => $this->translate('panel.updates.status.cannotCheck'),
]);
@ -46,7 +46,7 @@ class UpdatesController extends AbstractController
$backupper = new Backupper($this->config);
try {
$backupper->backup();
} catch (TranslatedException $e) {
} catch (TranslatedException) {
return JsonResponse::error($this->translate('panel.updates.status.cannotMakeBackup'), ResponseStatus::InternalServerError, [
'status' => $this->translate('panel.updates.status.cannotMakeBackup'),
]);
@ -54,7 +54,7 @@ class UpdatesController extends AbstractController
}
try {
$updater->update();
} catch (RuntimeException $e) {
} catch (RuntimeException) {
return JsonResponse::error($this->translate('panel.updates.status.cannotInstall'), ResponseStatus::InternalServerError, [
'status' => $this->translate('panel.updates.status.cannotInstall'),
]);

View File

@ -43,29 +43,29 @@ class UsersController extends AbstractController
{
$this->ensurePermission('users.create');
$data = $this->request->input();
$requestData = $this->request->input();
// Ensure no required data is missing
if (!$data->hasMultiple(['username', 'fullname', 'password', 'email', 'language'])) {
if (!$requestData->hasMultiple(['username', 'fullname', 'password', 'email', 'language'])) {
$this->panel()->notify($this->translate('panel.users.user.cannotCreate.varMissing'), 'error');
return $this->redirect($this->generateRoute('panel.users'));
}
// Ensure there isn't a user with the same username
if ($this->panel()->users()->has($data->get('username'))) {
if ($this->panel()->users()->has($requestData->get('username'))) {
$this->panel()->notify($this->translate('panel.users.user.cannotCreate.alreadyExists'), 'error');
return $this->redirect($this->generateRoute('panel.users'));
}
$userData = [
'username' => $data->get('username'),
'fullname' => $data->get('fullname'),
'hash' => Password::hash($data->get('password')),
'email' => $data->get('email'),
'language' => $data->get('language'),
'username' => $requestData->get('username'),
'fullname' => $requestData->get('fullname'),
'hash' => Password::hash($requestData->get('password')),
'email' => $requestData->get('email'),
'language' => $requestData->get('language'),
];
Yaml::encodeToFile($userData, FileSystem::joinPaths($this->config->get('system.panel.paths.accounts'), $data->get('username') . '.yaml'));
Yaml::encodeToFile($userData, FileSystem::joinPaths($this->config->get('system.panel.paths.accounts'), $requestData->get('username') . '.yaml'));
$this->panel()->notify($this->translate('panel.users.user.created'), 'success');
return $this->redirect($this->generateRoute('panel.users'));
@ -74,15 +74,15 @@ class UsersController extends AbstractController
/**
* Users@delete action
*/
public function delete(RouteParams $params): RedirectResponse
public function delete(RouteParams $routeParams): RedirectResponse
{
$this->ensurePermission('users.delete');
$user = $this->panel()->users()->get($params->get('user'));
$user = $this->panel()->users()->get($routeParams->get('user'));
try {
if (!$user) {
throw new TranslatedException(sprintf('User "%s" not found', $params->get('user')), 'panel.users.user.notFound');
throw new TranslatedException(sprintf('User "%s" not found', $routeParams->get('user')), 'panel.users.user.notFound');
}
if (!$this->user()->canDeleteUser($user)) {
throw new TranslatedException(
@ -109,13 +109,13 @@ class UsersController extends AbstractController
/**
* Users@profile action
*/
public function profile(RouteParams $params): Response
public function profile(RouteParams $routeParams): Response
{
$scheme = $this->app->schemes()->get('users.user');
$fields = $scheme->fields();
$user = $this->panel()->users()->get($params->get('user'));
$user = $this->panel()->users()->get($routeParams->get('user'));
if ($user === null) {
$this->panel()->notify($this->translate('panel.users.user.notFound'), 'error');
@ -206,9 +206,9 @@ class UsersController extends AbstractController
{
$imagesPath = FileSystem::joinPaths($this->config->get('system.panel.paths.assets'), '/images/users/');
$uploader = new FileUploader($this->config);
$fileUploader = new FileUploader($this->config);
$uploadedFile = $uploader->upload($file, $imagesPath, FileSystem::randomName());
$uploadedFile = $fileUploader->upload($file, $imagesPath, FileSystem::randomName());
if ($uploadedFile->type() === 'image') {
$userImageSize = $this->config->get('system.panel.userImageSize');

View File

@ -181,10 +181,7 @@ final class Panel
*/
public function assets(): Assets
{
if (isset($this->assets)) {
return $this->assets;
}
return $this->assets = new Assets($this->config->get('system.panel.paths.assets'), $this->realUri('/assets/'));
return $this->assets ?? ($this->assets = new Assets($this->config->get('system.panel.paths.assets'), $this->realUri('/assets/')));
}
/**
@ -195,7 +192,7 @@ final class Panel
public function availableTranslations(): array
{
/**
* @var array<string, string>
* @var array<string, string> $translations
*/
static $translations = [];
@ -267,10 +264,10 @@ final class Panel
protected function loadErrorHandler(): void
{
if ($this->config->get('system.errors.setHandlers')) {
$this->errors = $this->container->build(Controllers\ErrorsController::class);
set_exception_handler(function (Throwable $exception): void {
$this->errors->internalServerError($exception)->send();
throw $exception;
$this->errors = $this->container->build(ErrorsController::class);
set_exception_handler(function (Throwable $throwable): void {
$this->errors->internalServerError($throwable)->send();
throw $throwable;
});
}
}

Some files were not shown because too many files have changed in this diff Show More