20250203
This commit is contained in:
363
sources/classes/ActionState.php
Normal file
363
sources/classes/ActionState.php
Normal file
@@ -0,0 +1,363 @@
|
||||
<?php
|
||||
/**
|
||||
* Отключаем ненужные проверки.
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection, PhpUnused
|
||||
*/
|
||||
|
||||
namespace goodboyalex\php_components_pack\classes;
|
||||
|
||||
use Closure;
|
||||
use goodboyalex\php_components_pack\enums\MessageType;
|
||||
use goodboyalex\php_components_pack\interfaces\ISerializable;
|
||||
use goodboyalex\php_components_pack\models\ActionStateMessageModel;
|
||||
|
||||
/**
|
||||
* Состояние действия.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ActionState implements ISerializable
|
||||
{
|
||||
/**
|
||||
* @var mixed|null $Value Значение
|
||||
*/
|
||||
public mixed $Value;
|
||||
|
||||
/**
|
||||
* @var array $Messages Список информации
|
||||
*/
|
||||
private array $Messages = [];
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param mixed|null $defValue Значение по умолчанию
|
||||
*/
|
||||
public function __construct (mixed $defValue = null)
|
||||
{
|
||||
$this->Value = $defValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* При выводе GetStringMessages выводит только ошибки.
|
||||
*
|
||||
* @return Closure Возвращает функцию, проверяющую сообщение на соответствие типу.
|
||||
*/
|
||||
public static function GET_STRING_ERROR_ONLY (): Closure
|
||||
{
|
||||
return fn (ActionStateMessageModel $message)
|
||||
=> $message->MessageType === MessageType::Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* При выводе GetStringMessages выводит ошибки и предупреждения.
|
||||
*
|
||||
* @return Closure Возвращает функцию, проверяющую сообщение на соответствие типу.
|
||||
*/
|
||||
public static function GET_STRING_ERROR_AND_WARNING (): Closure
|
||||
{
|
||||
return fn (ActionStateMessageModel $message)
|
||||
=> $message->MessageType === MessageType::Error
|
||||
|| $message->MessageType === MessageType::Warning;
|
||||
}
|
||||
|
||||
/**
|
||||
* При выводе GetStringMessages выводит все сообщения.
|
||||
*
|
||||
* @return Closure Возвращает функцию, проверяющую сообщение на соответствие типу.
|
||||
*/
|
||||
public static function GET_STRING_ALL (): Closure
|
||||
{
|
||||
return fn (ActionStateMessageModel $message) => true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет данные из другого состояния.
|
||||
*
|
||||
* @param ActionState $state Другое состояние
|
||||
* @param bool $clearAllBefore Очищать сообщения перед добавлением (true) или просто добавить к текущим (false)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddState (ActionState $state, bool $clearAllBefore = false): void
|
||||
{
|
||||
// Если нужно очистить список сообщений
|
||||
if ($clearAllBefore)
|
||||
// - то очищаю список сообщений
|
||||
$this->Clear(fn (ActionStateMessageModel $message) => true);
|
||||
|
||||
// Добавляю сообщения из другого состояния
|
||||
$this->AddRange($state->GetMessages(function (ActionStateMessageModel $message)
|
||||
{
|
||||
return true;
|
||||
}));
|
||||
|
||||
// Добавляю значение
|
||||
$this->Value = $state->Value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает список сообщений, согласно условию.
|
||||
*
|
||||
* @param callable $predicate Условие выборки
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function Clear (callable $predicate): void
|
||||
{
|
||||
// Выбираю все сообщения, удовлетворяющие условию
|
||||
$list = $this->GetMessages($predicate);
|
||||
|
||||
// Удаляю их из списка
|
||||
$this->Messages = array_diff($this->Messages, $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбирает сообщения по условию predicate.
|
||||
*
|
||||
* @param callable $predicate Условие выборки
|
||||
*
|
||||
* @return array Список отобранных сообщений
|
||||
*/
|
||||
public function GetMessages (callable $predicate): array
|
||||
{
|
||||
// Получаю список элементов
|
||||
$list = [];
|
||||
|
||||
// Для каждого элемента
|
||||
foreach ($this->Messages as $actionStateMessage)
|
||||
// - если он подходит под условие
|
||||
if ($predicate($actionStateMessage))
|
||||
// - добавляю его в список
|
||||
$list[] = $actionStateMessage;
|
||||
|
||||
// Возвращаю список
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет список
|
||||
*
|
||||
* @param array $messages Список сообщений
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddRange (array $messages): void
|
||||
{
|
||||
$this->Messages = array_merge($this->Messages, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает список сообщений (параметр Message у каждого сообщения).
|
||||
*
|
||||
* @param callable $predicate Условие выборки
|
||||
* @param string $separator Разделитель
|
||||
*
|
||||
* @return string Список сообщений
|
||||
*/
|
||||
public function GetStringMessages (callable $predicate, string $separator = '\n'): string
|
||||
{
|
||||
// Делаю выборку
|
||||
$list = $this->GetMessages($predicate);
|
||||
|
||||
// Формирую список сообщений
|
||||
$result = [];
|
||||
|
||||
// Для каждого сообщения из выборки
|
||||
foreach ($list as $message) {
|
||||
// - если оно не нужного нам класса
|
||||
if (!$message instanceof ActionStateMessageModel)
|
||||
// -- то пропускаю
|
||||
continue;
|
||||
|
||||
// - добавляю сообщение
|
||||
$result[] = $message->Message;
|
||||
}
|
||||
|
||||
// Возвращаю список
|
||||
return implode($separator, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет сообщение о критической ошибке.
|
||||
*
|
||||
* @param string $message Сообщение
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddCritical (string $message): void
|
||||
{
|
||||
$this->Add(new ActionStateMessageModel(MessageType::Error, true, $message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление сообщения.
|
||||
*
|
||||
* @param ActionStateMessageModel $message Сообщение
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function Add (ActionStateMessageModel $message): void
|
||||
{
|
||||
$this->Messages[] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет сообщение об ошибке.
|
||||
*
|
||||
* @param string $message Сообщение
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddError (string $message): void
|
||||
{
|
||||
$this->Add(new ActionStateMessageModel(MessageType::Error, false, $message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет предупреждение.
|
||||
*
|
||||
* @param string $message Сообщение.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddWarning (string $message): void
|
||||
{
|
||||
$this->Add(new ActionStateMessageModel(MessageType::Warning, false, $message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет информационное сообщение.
|
||||
*
|
||||
* @param string $message Сообщение.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddInfo (string $message): void
|
||||
{
|
||||
$this->Add(new ActionStateMessageModel(MessageType::Info, false, $message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, есть ли информационные сообщения.
|
||||
*
|
||||
* @return bool Наличие сообщений
|
||||
*/
|
||||
public function HasInfos (): bool
|
||||
{
|
||||
return $this->Count(fn (ActionStateMessageModel $message) => $message->MessageType == MessageType::Info) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Количество сообщений, удовлетворяющих условию.
|
||||
*
|
||||
* @param callable $predicate Условие выборки
|
||||
*
|
||||
* @return int Число сообщений
|
||||
*/
|
||||
public function Count (callable $predicate): int
|
||||
{
|
||||
// Получаю список сообщений
|
||||
$list = $this->GetMessages($predicate);
|
||||
|
||||
// Возвращаю результат
|
||||
return count($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, успешно ли завершилась операция.
|
||||
*
|
||||
* @param bool $onlyCritical Игнорировать все некритические ошибки и предупреждения (не рекомендуется!)
|
||||
*
|
||||
* @return bool Успешно ли завершилась операция.
|
||||
*/
|
||||
public function IsSuccess (bool $onlyCritical = false): bool
|
||||
{
|
||||
// Если нужно учитывать только критические ошибки
|
||||
if ($onlyCritical)
|
||||
// - то проверяю наличие критических ошибок
|
||||
return !$this->HasErrors($onlyCritical);
|
||||
|
||||
// Возвращаю результат
|
||||
return !$this->HasErrors() && !$this->HasWarnings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, есть ли ошибки.
|
||||
*
|
||||
* @param bool $onlyCritical Учитывать только критические ошибки.
|
||||
*
|
||||
* @return bool Наличие ошибок.
|
||||
*/
|
||||
public function HasErrors (bool $onlyCritical = false): bool
|
||||
{
|
||||
return $this->Count(function (ActionStateMessageModel $message) use ($onlyCritical): bool
|
||||
{
|
||||
// - сравниваю тип сообщения
|
||||
if ($message->MessageType != MessageType::Error)
|
||||
// -- если не совпадает, то возвращаю false
|
||||
return false;
|
||||
|
||||
// - если нужно выводить только критические ошибки, а сообщение не критическое
|
||||
if ($onlyCritical && !$message->IsCritical)
|
||||
// -- то возвращаю false
|
||||
return false;
|
||||
|
||||
// Возвращаю true
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, есть ли предупреждения.
|
||||
*
|
||||
* @return bool Наличие предупреждений
|
||||
*/
|
||||
public function HasWarnings (): bool
|
||||
{
|
||||
return $this->Count(fn (ActionStateMessageModel $message) => $message->MessageType == MessageType::Warning) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function Serialize (): string
|
||||
{
|
||||
// Создаю список сообщений
|
||||
$list = [];
|
||||
|
||||
// Для каждого сообщения
|
||||
foreach ($this->Messages as $message)
|
||||
// - сериализую его и добавляю в список
|
||||
$list[] = $message->Serialize();
|
||||
|
||||
// Возвращаю результат
|
||||
return serialize($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function UnSerialize (string $serialized): void
|
||||
{
|
||||
// Очищаю список сообщений
|
||||
$this->Clear(fn (ActionStateMessageModel $message) => true);
|
||||
|
||||
// Десериализую список сообщений
|
||||
$list = unserialize($serialized);
|
||||
|
||||
// Для каждого сообщения
|
||||
foreach ($list as $messageSerialized) {
|
||||
// - создаю новое сообщение
|
||||
$message = new ActionStateMessageModel();
|
||||
// - десериализую его
|
||||
$message->UnSerialize($messageSerialized);
|
||||
// - добавляю в список
|
||||
$this->Add($message);
|
||||
}
|
||||
}
|
||||
}
|
335
sources/classes/ClassMapper.php
Normal file
335
sources/classes/ClassMapper.php
Normal file
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\classes;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Exception;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use stdClass;
|
||||
use UnitEnum;
|
||||
|
||||
/**
|
||||
* Класс сопоставлений классов и объектов.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ClassMapper
|
||||
{
|
||||
/**
|
||||
* @var array $DefaultOptions Настройки
|
||||
*/
|
||||
public const array DefaultOptions = [
|
||||
'ignored' => [],
|
||||
'allowed' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Передаёт одинаковые параметры из класса $from в класс $to, учитывая игнорируемые ($ignoredProperties) и
|
||||
* разрешенные ($allowedProperties) свойства.
|
||||
*
|
||||
* @param object $from Класс-донор
|
||||
* @param object $to Класс-приемник
|
||||
* @param array $options Параметры привязки свойств (см атрибут Bind).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function MapClass (object $from, object $to, array $options = self::DefaultOptions): void
|
||||
{
|
||||
// Если есть игнорируемые или разрешенные свойства
|
||||
if (!(count($options['ignored']) == 0 && count($options['allowed']) == 0))
|
||||
// -- то для каждого игнорируемого свойства
|
||||
foreach ($options['ignored'] as $ignoredProperty)
|
||||
// --- и если оно есть в массиве разрешенных
|
||||
if (in_array($ignoredProperty, $options['allowed']))
|
||||
// ---- то исключаю его из массива разрешенных
|
||||
unset($options['allowed'][array_search($ignoredProperty, $options['allowed'])]);
|
||||
|
||||
// Получаю массив свойств
|
||||
$properties = get_class_vars(get_class($from));
|
||||
|
||||
// Для каждого элемента массива
|
||||
foreach ($properties as $name => $value) {
|
||||
// - если свойство игнорируется
|
||||
if (in_array($name, $options['ignored']))
|
||||
// -- пропускаю
|
||||
continue;
|
||||
|
||||
// - если свойство не разрешено
|
||||
if (count($options['allowed']) > 0 && !in_array($name, $options['allowed']))
|
||||
// -- пропускаю
|
||||
continue;
|
||||
|
||||
// - если свойство есть в объекте
|
||||
if (property_exists($to, $name))
|
||||
// -- то присваиваю значение
|
||||
$to->$name = $from->$name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготавливает значения свойств класса.
|
||||
*
|
||||
* @param array $params Данные запроса.
|
||||
* @param ReflectionClass $classReflector Анализатор класса.
|
||||
* @param array $options Массив свойств привязки.
|
||||
*
|
||||
* @return array Массив данных класса.
|
||||
*/
|
||||
public static function PrepareClassProperties (array $params, ReflectionClass $classReflector,
|
||||
array $options = self::DefaultOptions): array
|
||||
{
|
||||
// Создаю массив данных класса
|
||||
$classData = [];
|
||||
|
||||
// Для каждого свойства класса
|
||||
foreach ($classReflector->getProperties() as $property) {
|
||||
// - получаю имя свойства
|
||||
$propertyName = $property->getName();
|
||||
|
||||
// - если это свойство задано в массиве параметров
|
||||
if (array_key_exists($propertyName, $params)) {
|
||||
// -- если задан массив разрешённых свойств
|
||||
if (!empty($options["allowed"]))
|
||||
// --- если свойство не разрешено
|
||||
if (!in_array($propertyName, $options["allowed"]))
|
||||
// ---- то пропускаю
|
||||
continue;
|
||||
|
||||
// -- если задан массив запрещённых свойств
|
||||
if (!empty($options["ignored"]))
|
||||
// --- если свойство должно игнорироваться
|
||||
if (in_array($propertyName, $options["ignored"]))
|
||||
// ---- то пропускаю
|
||||
continue;
|
||||
|
||||
// -- добавляю значение свойства в результат
|
||||
$classData[$propertyName] = $params[$propertyName];
|
||||
}
|
||||
else {
|
||||
// - в противном случае, пробегаю массив параметров
|
||||
foreach ($params as $key => $value) {
|
||||
// -- если в имени параметра есть разделитель "_"
|
||||
if (str_starts_with($key, $propertyName . "_")) {
|
||||
// -- разбиваю имя параметра на части
|
||||
$keyParts = explode("_", $key);
|
||||
|
||||
// -- добавляю значение свойства в результат
|
||||
self::GetClassParametersArrayParser($classData, $keyParts, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Возвращаю массив данных класса
|
||||
return $classData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Парсит массив свойств класса.
|
||||
*
|
||||
* @param array $source Исходный массив (он же и результат парсинга).
|
||||
* @param array $parametersKeys Массив имен свойств. Например, Page_Meta_Id должен быть разбит на
|
||||
* ["Page", "Meta", "Id"].
|
||||
* @param mixed $value Значение свойства.
|
||||
* @param array $options Массив параметров привязки свойств.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function GetClassParametersArrayParser (array &$source, array $parametersKeys, mixed $value,
|
||||
array $options = self::DefaultOptions): void
|
||||
{
|
||||
// Если массив имен свойств пустой
|
||||
if (empty($parametersKeys))
|
||||
// - то прерываю парсинг
|
||||
return;
|
||||
|
||||
// Получаю имя текущего свойства
|
||||
$currentName = array_shift($parametersKeys);
|
||||
|
||||
// Если текущего свойства нет в массиве
|
||||
if (!isset($source[$currentName]))
|
||||
// - то создаю его
|
||||
$source[$currentName] = [];
|
||||
|
||||
// Если массив имен свойств содержит только одно свойство
|
||||
if (count($parametersKeys) === 0) {
|
||||
// - если задан массив разрешённых свойств
|
||||
if (!empty($options["allowed"]))
|
||||
// --- если свойство не разрешено
|
||||
if (!in_array($currentName, $options["allowed"]))
|
||||
// ---- то пропускаю
|
||||
return;
|
||||
|
||||
// -- если задан массив запрещённых свойств
|
||||
if (!empty($options["ignored"]))
|
||||
// --- если свойство должно игнорироваться
|
||||
if (in_array($currentName, $options["ignored"]))
|
||||
// ---- то пропускаю
|
||||
return;
|
||||
|
||||
// - добавляю значение свойства в результат
|
||||
$source[$currentName] = $value;
|
||||
}
|
||||
else
|
||||
// - иначе продолжаю парсинг
|
||||
self::GetClassParametersArrayParser($source[$currentName], $parametersKeys, $value, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Переводит данные из массива в объект класса.
|
||||
*
|
||||
* @param string $className Имя класса
|
||||
* @param array $properties Массив данных
|
||||
*
|
||||
* @return mixed Объект класса
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function MapToClassProperty (string $className, array $properties): mixed
|
||||
{
|
||||
// Создаю
|
||||
try {
|
||||
$classReflector = new ReflectionClass($className);
|
||||
|
||||
// Создаю объект класса
|
||||
$classObj = new $className();
|
||||
|
||||
// Для каждого свойства класса
|
||||
foreach ($properties as $key => $value) {
|
||||
// - проверяю наличие свойства
|
||||
if (!$classReflector->hasProperty($key))
|
||||
// -- иду к следующему
|
||||
continue;
|
||||
|
||||
// - получаю данные про свойство
|
||||
// -- само свойство
|
||||
$property = $classReflector->getProperty($key);
|
||||
// -- тип свойства
|
||||
$propertyType = $property->getType();
|
||||
|
||||
// - если значение является классом
|
||||
if (!$propertyType->isBuiltin() && is_array($value)) {
|
||||
// -- присваиваю объект
|
||||
self::SetParameterToClass($classReflector, $key, $classObj,
|
||||
self::MapToClassProperty($propertyType->getName(), $value));
|
||||
|
||||
// -- следующий элемент
|
||||
continue;
|
||||
}
|
||||
|
||||
// - если значение является датой
|
||||
if ($classObj->$key instanceof DateTimeInterface) {
|
||||
// -- получаю дату
|
||||
$dateValue = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $value . " 00:00:00");
|
||||
|
||||
// -- если не получилось
|
||||
if ($dateValue === false)
|
||||
// --- то добавляю дату по умолчанию (1970-01-01 00:00:00)
|
||||
$dateValue = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', "1970-01-01 00:00:00");
|
||||
|
||||
// -- присваиваю дату
|
||||
self::SetParameterToClass($classReflector, $key, $classObj, $dateValue);
|
||||
|
||||
// -- следующий элемент
|
||||
continue;
|
||||
}
|
||||
|
||||
// - если значение является перечислением
|
||||
if ($classObj->$key instanceof UnitEnum) {
|
||||
// -- если значение уже является перечислением
|
||||
if ($value instanceof UnitEnum)
|
||||
// --- присваиваю перечисление
|
||||
self::SetParameterToClass($classReflector, $key, $classObj, $value);
|
||||
else
|
||||
// -- иначе
|
||||
self::SetParameterToClass($classReflector, $key, $classObj,
|
||||
is_numeric($value) ? $classObj->$key::FromInt($value) : $classObj->$key::FromName($value));
|
||||
|
||||
// -- следующий элемент
|
||||
continue;
|
||||
}
|
||||
|
||||
// - если значение является NULL
|
||||
if ($value == "null") {
|
||||
// -- присваиваю NULL
|
||||
self::SetParameterToClass($classReflector, $key, $classObj,
|
||||
is_array($key) ? [] : null);
|
||||
|
||||
// -- следующий элемент
|
||||
continue;
|
||||
}
|
||||
|
||||
// - присваиваю значение
|
||||
self::SetParameterToClass($classReflector, $key, $classObj, $value);
|
||||
}
|
||||
|
||||
// Возвращаю объект класса
|
||||
return $classObj;
|
||||
}
|
||||
catch (Exception $exception) {
|
||||
throw new Exception($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Присваивает значение параметра объекту класса.
|
||||
*
|
||||
* @param ReflectionClass $classReflector Рефлектор класса.
|
||||
* @param string $propertyName Имя свойства.
|
||||
* @param mixed $classObj Объект класса.
|
||||
* @param mixed $value Значение.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function SetParameterToClass (ReflectionClass $classReflector, string $propertyName,
|
||||
mixed $classObj, mixed $value): void
|
||||
{
|
||||
try {
|
||||
// Получаю свойство
|
||||
$property = $classReflector->getProperty($propertyName);
|
||||
|
||||
/**
|
||||
* Устанавливаю доступ значения свойства.
|
||||
*
|
||||
* @noinspection PhpExpressionResultUnusedInspection
|
||||
*/
|
||||
$property->setAccessible(true);
|
||||
|
||||
// Если значение null
|
||||
if ($value == null || $value == "null")
|
||||
// - то присваиваю значение по умолчанию
|
||||
$value = self::GetDefaults($property->getType()->getName());
|
||||
|
||||
// Присваиваю значение
|
||||
$property->setValue($classObj, $value);
|
||||
}
|
||||
catch (ReflectionException $exception) {
|
||||
// Выбрасываю исключение
|
||||
throw new Exception($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает значение по умолчанию для разных типов данных.
|
||||
*
|
||||
* @param string $typeName Имя типа данных.
|
||||
*
|
||||
* @return mixed|null Результат.
|
||||
*/
|
||||
public static function GetDefaults (string $typeName): mixed
|
||||
{
|
||||
return match ($typeName) {
|
||||
'int' => 0,
|
||||
'float' => 0.0,
|
||||
'bool' => false,
|
||||
'string' => '',
|
||||
'array' => [],
|
||||
'object' => new stdClass(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
60
sources/classes/ObjectArray.php
Normal file
60
sources/classes/ObjectArray.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\classes;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use goodboyalex\php_components_pack\interfaces\ISerializable;
|
||||
use goodboyalex\php_components_pack\traits\ObjectArray\ObjectArrayBasicTrait;
|
||||
use goodboyalex\php_components_pack\traits\ObjectArray\ObjectArrayConstantsTrait;
|
||||
use goodboyalex\php_components_pack\traits\ObjectArray\ObjectArrayLINQTrait;
|
||||
use goodboyalex\php_components_pack\traits\ObjectArray\ObjectArraySearchAndSortTrait;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Класс, описывающий массив объектов.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ObjectArray implements ArrayAccess, IteratorAggregate, Countable, ISerializable
|
||||
{
|
||||
/**
|
||||
* @var array $Container Массив объектов, хранящихся в данном классе.
|
||||
*/
|
||||
private array $Container;
|
||||
|
||||
// Реализация наследуемых интерфейсов и классов
|
||||
use ObjectArrayBasicTrait;
|
||||
|
||||
// Константы
|
||||
use ObjectArrayConstantsTrait;
|
||||
|
||||
// Поиск элемента
|
||||
use ObjectArraySearchAndSortTrait;
|
||||
|
||||
// LINQ-подобные методы
|
||||
use ObjectArrayLINQTrait;
|
||||
|
||||
/**
|
||||
* Конструктор класса.
|
||||
*
|
||||
* @param array $array Массив объектов, который будет храниться в данном классе.
|
||||
*/
|
||||
public function __construct (array $array = [])
|
||||
{
|
||||
$this->Container = $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает массив объектов, хранящихся в данном классе.
|
||||
*
|
||||
* @return array Массив объектов, хранящихся в данном классе.
|
||||
*/
|
||||
public function ToArray (): array
|
||||
{
|
||||
return $this->Container;
|
||||
}
|
||||
}
|
39
sources/enums/MessageType.php
Normal file
39
sources/enums/MessageType.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\enums;
|
||||
|
||||
use goodboyalex\php_components_pack\traits\EnumExtensionsTrait;
|
||||
|
||||
/**
|
||||
* Перечисление типов сообщений.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
enum MessageType: int
|
||||
{
|
||||
// Подключаю расширение для Enum
|
||||
use EnumExtensionsTrait;
|
||||
|
||||
/**
|
||||
* Успех
|
||||
*/
|
||||
case Success = 0;
|
||||
|
||||
/**
|
||||
* Информация
|
||||
*/
|
||||
case Info = 1;
|
||||
|
||||
/**
|
||||
* Предупреждение
|
||||
*/
|
||||
case Warning = 2;
|
||||
|
||||
/**
|
||||
* Ошибка
|
||||
*/
|
||||
case Error = 3;
|
||||
}
|
60
sources/extensions/ArrayExtension.php
Normal file
60
sources/extensions/ArrayExtension.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\extensions;
|
||||
|
||||
/**
|
||||
* Расширение массивов.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ArrayExtension
|
||||
{
|
||||
/**
|
||||
* Удаляет пустые строки в массиве.
|
||||
*
|
||||
* @param array $array Исходный массив
|
||||
* @param bool $reOrder Переиндексировать массив
|
||||
*
|
||||
* @return array Результирующим массив без пустых строк
|
||||
*/
|
||||
public static function RemoveEmpties (array $array, bool $reOrder = false): array
|
||||
{
|
||||
// Удаляем пустые строки
|
||||
$result = array_filter($array, fn ($value) => !is_null($value) && $value !== '');
|
||||
|
||||
// Переиндексируем массив
|
||||
if ($reOrder)
|
||||
$result = array_values($result);
|
||||
|
||||
// Возвращаем результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выделяет в смешанном массиве ассоциативный массив
|
||||
*
|
||||
* @param array $array Исходный массив
|
||||
*
|
||||
* @return array Ассоциативный массив, содержащийся в исходном массиве
|
||||
*/
|
||||
public static function GetAssociativePart (array $array): array
|
||||
{
|
||||
// Фильтруем массив, оставляя только элементы со строковыми ключами
|
||||
return array_filter($array, fn ($key) => self::IsStringKey($key), ARRAY_FILTER_USE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция для проверки, является ли ключ строковым
|
||||
*
|
||||
* @param mixed $key Ключ
|
||||
*
|
||||
* @return bool Является ли ключ строковым типом
|
||||
*/
|
||||
public static function IsStringKey (mixed $key): bool
|
||||
{
|
||||
return !is_int($key);
|
||||
}
|
||||
}
|
147
sources/extensions/GUIDExtension.php
Normal file
147
sources/extensions/GUIDExtension.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\extensions;
|
||||
|
||||
use Random\RandomException;
|
||||
|
||||
/**
|
||||
* Расширение Guid.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class GUIDExtension
|
||||
{
|
||||
/**
|
||||
* Пустой Guid.
|
||||
*/
|
||||
public const string GUIDEmpty = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
/**
|
||||
* Генерирует Guid.
|
||||
*
|
||||
* @return string Сгенерированный Guid.
|
||||
*/
|
||||
public static function Generate (): string
|
||||
{
|
||||
// Цикл создания Guid
|
||||
do
|
||||
$guid = self::DoGenerate();
|
||||
// - пока Guid не будет корректен
|
||||
while (!self::Validate($guid));
|
||||
|
||||
// Возвращаем Guid
|
||||
return $guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует Guid.
|
||||
*
|
||||
* @return string Сгенерированный Guid.
|
||||
*/
|
||||
private static function DoGenerate (): string
|
||||
{
|
||||
try {
|
||||
return sprintf(
|
||||
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
// 32 bits for "time_low"
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff),
|
||||
|
||||
// 16 bits for "time_mid"
|
||||
random_int(0, 0xffff),
|
||||
|
||||
// 16 bits for "time_hi_and_version",
|
||||
// four most significant bits holds version number 4
|
||||
random_int(0, 0x0fff) | 0x4000,
|
||||
|
||||
// 16 bits, 8 bits for "clk_seq_hi_res",
|
||||
// 8 bits for "clk_seq_low",
|
||||
// two most significant bits holds zero and one for variant DCE1.1
|
||||
random_int(0, 0x3fff) | 0x8000,
|
||||
|
||||
// 48 bits for "node"
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff)
|
||||
);
|
||||
}
|
||||
catch (RandomException) {
|
||||
return self::GUIDEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет Guid на корректность.
|
||||
*
|
||||
* @param string|null $str Guid на проверку
|
||||
*
|
||||
* @return bool Корректен ли Guid.
|
||||
*/
|
||||
public static function Validate (?string $str): bool
|
||||
{
|
||||
// Если Guid пустой
|
||||
if (StringExtension::IsNullOrWhitespace($str))
|
||||
// - возвращаем false
|
||||
return false;
|
||||
|
||||
// Проверяем длину
|
||||
$isLenCorrect = strlen($str) == 36;
|
||||
|
||||
// Если длина не корректна
|
||||
if (!$isLenCorrect)
|
||||
// - возвращаем false
|
||||
return false;
|
||||
|
||||
// Разбиваем на части
|
||||
$explodedStr = explode("-", $str);
|
||||
|
||||
// Если количество частей не равно 5
|
||||
if (count($explodedStr) !== 5)
|
||||
// - возвращаем false
|
||||
return false;
|
||||
|
||||
// Проверяем длину каждой части
|
||||
// - первая часть должна быть длиной 8 символов
|
||||
if (strlen($explodedStr[0]) !== 8)
|
||||
// -- возвращаем false
|
||||
return false;
|
||||
|
||||
// - вторая часть должна быть длиной 4 символа
|
||||
if (strlen($explodedStr[1]) !== 4)
|
||||
// -- возвращаем false
|
||||
return false;
|
||||
|
||||
// - третья часть должна быть длиной 4 символа
|
||||
if (strlen($explodedStr[2]) !== 4)
|
||||
// -- возвращаем false
|
||||
return false;
|
||||
|
||||
// - четвертая часть должна быть длиной 4 символа
|
||||
if (strlen($explodedStr[3]) !== 4)
|
||||
// -- возвращаем false
|
||||
return false;
|
||||
|
||||
// - пятая часть должна быть длиной 12 символов
|
||||
if (strlen($explodedStr[4]) !== 12)
|
||||
// -- возвращаем false
|
||||
return false;
|
||||
|
||||
// Проверка пройдена
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет Guid на пустоту.
|
||||
*
|
||||
* @param string|null $str Guid на проверку
|
||||
*
|
||||
* @return bool Пустой ли GUID
|
||||
*/
|
||||
public static function IsNotValidOrEmpty (?string $str): bool
|
||||
{
|
||||
return !self::Validate($str) || $str == self::GUIDEmpty;
|
||||
}
|
||||
}
|
210
sources/extensions/StringExtension.php
Normal file
210
sources/extensions/StringExtension.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\extensions;
|
||||
|
||||
/**
|
||||
* Расширение строк.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class StringExtension
|
||||
{
|
||||
/**
|
||||
* @var array|string[] $RussianLetters Набор русских букв
|
||||
*/
|
||||
private static array $RussianLetters =
|
||||
[
|
||||
'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з', 'и', 'й',
|
||||
'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф',
|
||||
'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я',
|
||||
'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й',
|
||||
'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф',
|
||||
'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array|string[] $RussianLettersTransliteration Набор русских букв в транслитерации
|
||||
*/
|
||||
private static array $RussianLettersTransliteration =
|
||||
[
|
||||
"a", "b", "v", "g", "d", "e", "yo", "zh", "z", "i", "j",
|
||||
"k", "l", "m", "n", "o", "p", "r", "s", "t", "u", "f",
|
||||
"h", "c", "ch", "sh", "sch", "j", "i", "j", "e", "yu", "ya",
|
||||
"A", "B", "V", "G", "D", "E", "Yo", "Zh", "Z", "I", "J",
|
||||
"K", "L", "M", "N", "O", "P", "R", "S", "T", "U", "F",
|
||||
"H", "C", "Ch", "Sh", "Sch", "J", "I", "J", "E", "Yu", "Ya"
|
||||
];
|
||||
|
||||
/**
|
||||
* Сравнивает две строки.
|
||||
*
|
||||
* @param string|null $str1 Первая строка.
|
||||
* @param string|null $str2 Вторая строка.
|
||||
* @param bool $ignoreCase Игнорировать регистр.
|
||||
*
|
||||
* @return int Результат сравнения. Возвращаемые значения:
|
||||
*
|
||||
* -1 | значение $str1 меньше значения $str2.
|
||||
* 0 | значения $str1 и $str2 равны.
|
||||
* 1 | значение $str1 больше значения $str2.
|
||||
*/
|
||||
public static function Compare (string|null $str1, string|null $str2, bool $ignoreCase = false): int
|
||||
{
|
||||
// Если оба пусты
|
||||
if (self::IsNullOrWhitespace($str1) && self::IsNullOrWhitespace($str2))
|
||||
// - то равны
|
||||
return 0;
|
||||
|
||||
// Если первый из них не пуст, а второй пуст
|
||||
if (!self::IsNullOrWhitespace($str1) && self::IsNullOrWhitespace($str2))
|
||||
// - то первый больше
|
||||
return 1;
|
||||
|
||||
// Если первый из них пуст, а второй не пуст
|
||||
if (!self::IsNullOrWhitespace($str1) && self::IsNullOrWhitespace($str2))
|
||||
// - то первый меньше
|
||||
return -1;
|
||||
|
||||
// Если нужно игнорировать регистр
|
||||
if ($ignoreCase)
|
||||
// - то сравниваем по "человечески" без учёта регистра
|
||||
return strnatcasecmp($str1, $str2);
|
||||
|
||||
// Иначе сравниваем по "человечески" с учётом регистра
|
||||
return strnatcmp($str1, $str2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, пуста ли строка, содержит ли вместо текста только пробелы.
|
||||
*
|
||||
* @param string|null $str Проверяемая строка.
|
||||
*
|
||||
* @return bool Результат проверки.
|
||||
*/
|
||||
public static function IsNullOrWhitespace (string|null $str): bool
|
||||
{
|
||||
return self::IsNullOrEmpty($str) || trim($str) === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, пуста ли строка.
|
||||
*
|
||||
* @param string|null $str Проверяемая строка.
|
||||
*
|
||||
* @return bool Результат проверки.
|
||||
*/
|
||||
public static function IsNullOrEmpty (string|null $str): bool
|
||||
{
|
||||
return is_null($str) || $str === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертация в латиницу.
|
||||
*
|
||||
* @param string $source Исходное слово или выражение
|
||||
*
|
||||
* @return string Транслитерированное слово или выражение
|
||||
*/
|
||||
public static function ConvertToLatin (string $source): string
|
||||
{
|
||||
// Создаю результат
|
||||
$result = "";
|
||||
|
||||
// Длина исходного слова
|
||||
$length = mb_strlen($source);
|
||||
|
||||
// Для каждой буквы или символа из слова
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
// - получаю букву или символ
|
||||
$letter = mb_substr($source, $i, 1);
|
||||
|
||||
// - если это буква из русского алфавита
|
||||
if (self::IsRussianLetter($letter)) {
|
||||
// -- транслитерирую эту букву
|
||||
$resultTransliteration = self::TransliterationFromRussian($letter);
|
||||
|
||||
// -- если транслитерация не удалась
|
||||
if ($resultTransliteration === false)
|
||||
// --- вывожу оригинальную букву
|
||||
$result = $result . $letter;
|
||||
else
|
||||
// --- вывожу транслитерированную букву
|
||||
$result = $result . $resultTransliteration;
|
||||
|
||||
// -- и иду к следующей букве или символу
|
||||
continue;
|
||||
}
|
||||
|
||||
// - если до сих пор буква или символ не обработаны, то возвращаю их
|
||||
$result = $result . $letter;
|
||||
}
|
||||
|
||||
// Вывожу результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, является ли буква русской.
|
||||
*
|
||||
* @param string $letter Буква
|
||||
*
|
||||
* @return bool Является ли буква русской
|
||||
*/
|
||||
private static function IsRussianLetter (string $letter): bool
|
||||
{
|
||||
return in_array($letter, self::$RussianLetters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получаю транслитерированную букву русского алфавита.
|
||||
*
|
||||
* @param string $letter Буква
|
||||
*
|
||||
* @return false|string Транслитерированная буква или false, если массивы не совпадают или буква не содержится
|
||||
* в массивах.
|
||||
*/
|
||||
private static function TransliterationFromRussian (string $letter): false|string
|
||||
{
|
||||
// Если размерность массивов разная
|
||||
if (count(self::$RussianLetters) !== count(self::$RussianLettersTransliteration))
|
||||
// - то вывожу ошибку
|
||||
return false;
|
||||
|
||||
// Получаю индекс буквы
|
||||
$ind = array_search($letter, self::$RussianLetters, true);
|
||||
|
||||
// Если буква не найдена
|
||||
if ($ind === false)
|
||||
// - то вывожу ошибку
|
||||
return false;
|
||||
|
||||
// Получаю транслитерированную букву
|
||||
return self::$RussianLettersTransliteration[$ind];
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрезает строку до указанных в параметре $maxLength символов
|
||||
*
|
||||
* @param string $text Исходный текст
|
||||
* @param int $maxLength Максимальная длина текста
|
||||
* @param string $endDots Суффикс, которым завершается обрезанная строка
|
||||
*
|
||||
* @return string Обрезанный текст
|
||||
*/
|
||||
public static function GetShortText (string $text, int $maxLength, string $endDots = ''): string
|
||||
{
|
||||
// Если длина текста меньше максимальной
|
||||
if (mb_strlen($text) <= $maxLength)
|
||||
// - то возвращаю исходный текст
|
||||
return $text;
|
||||
|
||||
// Если длина текста больше максимальной, то получаю длину текста без суффикса
|
||||
$lengthWithoutEndDots = $maxLength - mb_strlen($endDots);
|
||||
|
||||
// Возвращаю обрезанный текст
|
||||
return mb_substr($text, 0, $lengthWithoutEndDots) . $endDots;
|
||||
}
|
||||
}
|
30
sources/interfaces/ISerializable.php
Normal file
30
sources/interfaces/ISerializable.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\interfaces;
|
||||
|
||||
/**
|
||||
* Интерфейс поддержки правильной сериализации объектов.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
interface ISerializable
|
||||
{
|
||||
/**
|
||||
* Сериализация модели
|
||||
*
|
||||
* @return string Сериализованная модель
|
||||
*/
|
||||
public function Serialize (): string;
|
||||
|
||||
/**
|
||||
* Десериализует модель
|
||||
*
|
||||
* @param string $serialized Сериализованная модель
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function UnSerialize (string $serialized): void;
|
||||
}
|
108
sources/models/ActionStateMessageModel.php
Normal file
108
sources/models/ActionStateMessageModel.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\models;
|
||||
|
||||
use Exception;
|
||||
use goodboyalex\php_components_pack\classes\ClassMapper;
|
||||
use goodboyalex\php_components_pack\enums\MessageType;
|
||||
use goodboyalex\php_components_pack\interfaces\ISerializable;
|
||||
use UnitEnum;
|
||||
|
||||
/**
|
||||
* Класс сообщения состояния.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ActionStateMessageModel implements ISerializable
|
||||
{
|
||||
/**
|
||||
* @var MessageType $MessageType Тип сообщения
|
||||
*/
|
||||
public MessageType $MessageType;
|
||||
|
||||
/**
|
||||
* @var bool $IsCritical Критичность сообщения
|
||||
*/
|
||||
public bool $IsCritical;
|
||||
|
||||
/**
|
||||
* @var string $Message Текст сообщения
|
||||
*/
|
||||
public string $Message;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param MessageType $type Тип сообщения
|
||||
* @param bool $isCritical Критичность сообщения
|
||||
* @param string $message Текст сообщения
|
||||
*/
|
||||
public function __construct (MessageType $type = MessageType::Info, bool $isCritical = false, string $message = "")
|
||||
{
|
||||
$this->MessageType = $type;
|
||||
$this->IsCritical = $isCritical;
|
||||
$this->Message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function UnSerialize (string $serialized): void
|
||||
{
|
||||
// Десериализую массив
|
||||
$thisNew = unserialize($serialized);
|
||||
|
||||
try {
|
||||
// Получаю класс
|
||||
$class = ClassMapper::MapToClassProperty(get_class($this), $thisNew);
|
||||
}
|
||||
catch (Exception) {
|
||||
$class = new ActionStateMessageModel();
|
||||
}
|
||||
|
||||
// Заполняю текущий класс
|
||||
ClassMapper::MapClass($class, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function Serialize (): string
|
||||
{
|
||||
// Создаю массив результата
|
||||
$result = [];
|
||||
|
||||
// Получаю свойства класса
|
||||
$properties = get_object_vars($this);
|
||||
|
||||
// Для каждого свойства класса
|
||||
foreach ($properties as $key => $value) {
|
||||
|
||||
// - если значение является перечислением
|
||||
if ($value instanceof UnitEnum) {
|
||||
// -- получаю перечисление
|
||||
$result[$key] = $value->name;
|
||||
|
||||
// -- следующий элемент
|
||||
continue;
|
||||
}
|
||||
|
||||
// - если значение является NULL
|
||||
if ($value == null) {
|
||||
// -- присваиваю NULL
|
||||
$result[$key] = "null";
|
||||
// -- следующий элемент
|
||||
continue;
|
||||
}
|
||||
|
||||
// - присваиваю значение
|
||||
$result[$key] = $value;
|
||||
}
|
||||
|
||||
// Сериализую массив и вывожу его
|
||||
return serialize($result);
|
||||
}
|
||||
}
|
80
sources/traits/EnumExtensionsTrait.php
Normal file
80
sources/traits/EnumExtensionsTrait.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\traits;
|
||||
|
||||
use goodboyalex\php_components_pack\enums\MessageType;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionEnum;
|
||||
use ReflectionException;
|
||||
|
||||
/**
|
||||
* Расширение перечислений.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
trait EnumExtensionsTrait
|
||||
{
|
||||
/**
|
||||
* Получает значение перечисления по его имени.
|
||||
*
|
||||
* @param string $name Имя значения перечисления
|
||||
*
|
||||
* @return MessageType|EnumExtensionsTrait Перечисление
|
||||
*/
|
||||
public static function FromName (string $name): self
|
||||
{
|
||||
// Создаём экземпляр
|
||||
$reflection = new ReflectionEnum(static::class);
|
||||
|
||||
// Проверяем, есть ли такая переменная
|
||||
if (!$reflection->hasCase($name))
|
||||
// - если нет - ошибка
|
||||
throw new InvalidArgumentException(sprintf("Enumeration name '%s' does not exist!", $name));
|
||||
|
||||
try {
|
||||
$result = $reflection->getCase($name)->getValue();
|
||||
}
|
||||
catch (ReflectionException) {
|
||||
|
||||
$result = new static();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает значение перечисления
|
||||
*
|
||||
* @return string Значение перечисления
|
||||
*/
|
||||
public function GetValue (): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертирует в перечисление из int.
|
||||
*
|
||||
* @param int $value Значение перечисления в формате int
|
||||
*
|
||||
* @return MessageType|EnumExtensionsTrait Объект
|
||||
* перечисления
|
||||
*/
|
||||
public static function FromInt (int $value): self
|
||||
{
|
||||
return self::tryFrom($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Переводит тип в INT
|
||||
*
|
||||
* @return int Значение типа в INT
|
||||
*/
|
||||
public function ToInt (): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
90
sources/traits/ObjectArray/ObjectArrayBasicTrait.php
Normal file
90
sources/traits/ObjectArray/ObjectArrayBasicTrait.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\traits\ObjectArray;
|
||||
|
||||
use ArrayIterator;
|
||||
|
||||
/**
|
||||
* Часть кода класса ObjectArray, отвечающая за функции из наследуемых интерфейсов и классов.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
trait ObjectArrayBasicTrait
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetExists (mixed $offset): bool
|
||||
{
|
||||
return isset($this->Container[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetGet (mixed $offset): mixed
|
||||
{
|
||||
return $this->container[$offset] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetSet (mixed $offset, mixed $value): void
|
||||
{
|
||||
// Если смещение не указано
|
||||
if (is_null($offset))
|
||||
// - то добавляем объект в конец массива.
|
||||
$this->Container[] = $value;
|
||||
else
|
||||
// - иначе добавляем объект по указанному смещению.
|
||||
$this->Container[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetUnset (mixed $offset): void
|
||||
{
|
||||
unset($this->Container[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIterator (): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->Container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Существует ли элемент по указанному смещению.
|
||||
*
|
||||
* @param mixed $offset Смещение.
|
||||
*
|
||||
* @return bool Существует ли элемент по указанному смещению.
|
||||
*/
|
||||
public function __isset (mixed $offset): bool
|
||||
{
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function Serialize (): string
|
||||
{
|
||||
return serialize($this->Container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function UnSerialize (string $serialized): void
|
||||
{
|
||||
$this->Container = unserialize($serialized);
|
||||
}
|
||||
}
|
26
sources/traits/ObjectArray/ObjectArrayConstantsTrait.php
Normal file
26
sources/traits/ObjectArray/ObjectArrayConstantsTrait.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\traits\ObjectArray;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Часть кода класса ObjectArray, отвечающая за константы.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
trait ObjectArrayConstantsTrait
|
||||
{
|
||||
/**
|
||||
* @var Closure Функция сравнения объектов по умолчанию.
|
||||
*/
|
||||
public Closure $DefaultComparerFunction {
|
||||
get {
|
||||
return fn (mixed $currentPropertyValue, mixed $searchPropertyValue): bool
|
||||
=> $currentPropertyValue === $searchPropertyValue;
|
||||
}
|
||||
}
|
||||
}
|
283
sources/traits/ObjectArray/ObjectArrayLINQTrait.php
Normal file
283
sources/traits/ObjectArray/ObjectArrayLINQTrait.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\traits\ObjectArray;
|
||||
|
||||
use goodboyalex\php_components_pack\classes\ObjectArray;
|
||||
|
||||
/**
|
||||
* Часть кода класса ObjectArray, отвечающая за LINQ-подобные операции.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
trait ObjectArrayLINQTrait
|
||||
{
|
||||
/**
|
||||
* Возвращает объект, значение свойства которого минимально.
|
||||
*
|
||||
* @param callable $itemValuePredicate Функция, возвращающая значение свойства объекта.
|
||||
* Параметром является объект массива.
|
||||
*
|
||||
* @return mixed|null Объект, значение свойства которого минимально
|
||||
*/
|
||||
public function MinBy (callable $itemValuePredicate): mixed
|
||||
{
|
||||
return array_reduce($this->Container,
|
||||
fn ($min, $item) => $min === null || $itemValuePredicate($item) < $itemValuePredicate($min) ? $item : $min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект, значение свойства которого максимально.
|
||||
*
|
||||
* @param callable $itemValuePredicate Функция, возвращающая значение свойства объекта.
|
||||
* Параметром является объект массива.
|
||||
*
|
||||
* @return mixed|null Объект, значение свойства которого максимально.
|
||||
*/
|
||||
public function MaxBy (callable $itemValuePredicate): mixed
|
||||
{
|
||||
return array_reduce($this->Container,
|
||||
fn ($max, $item) => $max === null || $itemValuePredicate($item) > $itemValuePredicate($max) ? $item : $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выделяет из массива объектов объект, удовлетворяющий условию. Если объектов несколько, то выбирается первый
|
||||
* встречающийся объект из них. Если объектов нет, вернёт false.
|
||||
*
|
||||
* @param callable|null $selectCondition Функция выборки объектов <code>fn (mixed $item): bool</code>. Вместо неё
|
||||
* можно использовать null, тогда функция выберет текущий объект. По умолчанию, null.
|
||||
*
|
||||
* @return false|mixed Выбранный объект, удовлетворяющий условию, или false, если таких нет.
|
||||
*/
|
||||
public function GetRow (?callable $selectCondition = null): mixed
|
||||
{
|
||||
// Если условие не задано
|
||||
if ($selectCondition === null || !is_callable($selectCondition)) {
|
||||
// - то если массив не пуст
|
||||
if (count($this->Container) > 0)
|
||||
// -- возвращаем первый элемент
|
||||
return $this->Container[0];
|
||||
else
|
||||
// -- иначе возвращаем false
|
||||
return false;
|
||||
}
|
||||
|
||||
// Получаем результаты выборки из массива по условию $selectCondition
|
||||
$result = $this->GetRows($selectCondition);
|
||||
|
||||
// Если результат не найден
|
||||
if (count($result) == 0)
|
||||
// - возвращаем false
|
||||
return false;
|
||||
|
||||
// Возвращаем результат
|
||||
return $result[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Выделяет из массива объектов объекты, удовлетворяющие условию.
|
||||
*
|
||||
* @param callable|null $selectCondition Функция выборки объектов <code>fn (mixed $item): bool</code>. Вместо неё
|
||||
* можно использовать null, тогда функция будет выбирать все объекты. По умолчанию, null.
|
||||
*
|
||||
* @return ObjectArray Массив объектов, удовлетворяющих условию
|
||||
*/
|
||||
public function GetRows (?callable $selectCondition = null): ObjectArray
|
||||
{
|
||||
// Если условие не задано
|
||||
if ($selectCondition === null || !is_callable($selectCondition))
|
||||
// - то возвращаем все элементы
|
||||
return new ObjectArray($this->Container);
|
||||
|
||||
// Создаём результирующий массив
|
||||
$result = [];
|
||||
|
||||
// Проходим по массиву
|
||||
foreach ($this->Container as $item) {
|
||||
// - пропускаем не объекты
|
||||
if (!is_object($item))
|
||||
continue;
|
||||
|
||||
// - если объект удовлетворяет условию
|
||||
if ($selectCondition($item))
|
||||
// -- добавляем его в результат
|
||||
$result[] = $item;
|
||||
}
|
||||
|
||||
// Возвращаем результат
|
||||
return new ObjectArray($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение значение единичного поля. Если полей по выборке будет больше одного, то вернёт первое из них.
|
||||
*
|
||||
* @param string $column Требуемый столбец
|
||||
* @param callable|null $wherePredicate Условие выборки <code>fn (mixed $item): bool</code>, которое проверяет,
|
||||
* подходит элемент или нет.
|
||||
*
|
||||
* @return mixed|null Результат запроса или null в случае ошибки
|
||||
*/
|
||||
public function GetValue (string $column, ?callable $wherePredicate = null): mixed
|
||||
{
|
||||
// Получаю колонку
|
||||
$result = $this->GetColumn($column, $wherePredicate);
|
||||
|
||||
// Если результат пуст
|
||||
if (count($result) == 0)
|
||||
// -- возвращаю null
|
||||
return null;
|
||||
|
||||
// Возвращаю результат
|
||||
return $result[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает колонку в массиве данных.
|
||||
*
|
||||
* @param string $column Имя колонки.
|
||||
* @param callable|null $wherePredicate Условие выборки <code>fn (mixed $item): bool</code>, которое проверяет,
|
||||
* подходит элемент или нет.
|
||||
*
|
||||
* @return array Ассоциированный массив с результатом выборки.
|
||||
*/
|
||||
public function GetColumn (string $column, ?callable $wherePredicate = null): array
|
||||
{
|
||||
// Создаю результат
|
||||
$result = [];
|
||||
|
||||
// Прохожу по массиву
|
||||
foreach ($this->Container as $item) {
|
||||
// - пропускаю не объекты
|
||||
if (!is_object($item))
|
||||
continue;
|
||||
|
||||
// - пропускаю не имеющие требуемого свойства
|
||||
if (!property_exists($item, $column))
|
||||
continue;
|
||||
|
||||
// - пропускаю не удовлетворяющие условию
|
||||
if ($wherePredicate !== null && !$wherePredicate($item))
|
||||
continue;
|
||||
|
||||
// - добавляю значение свойства в результат
|
||||
$result[] = $item->$column;
|
||||
}
|
||||
|
||||
// Возвращаю результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет данные в строке\ массива.
|
||||
*
|
||||
* @param mixed $setItem Заменяемые элемент.
|
||||
* @param callable $wherePredicate Условие выборки <code>fn (mixed $item): bool</code>, которое проверяет,
|
||||
* подходит элемент или нет.
|
||||
*
|
||||
* @return bool Результат выполнения
|
||||
*/
|
||||
public function Update (array $setItems, callable $wherePredicate): bool
|
||||
{
|
||||
// Удаляю элементы
|
||||
$result = $this->Delete($wherePredicate);
|
||||
|
||||
// Добавляю новые элементы
|
||||
foreach ($setItems as $item)
|
||||
$this->Container[] = $item;
|
||||
|
||||
// Возвращаю результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет строки по условию.
|
||||
*
|
||||
* @param callable|null $wherePredicate Условие выборки <code>fn (mixed $item): bool</code>, которое проверяет,
|
||||
* подходит элемент или нет.
|
||||
*
|
||||
* @return bool Результат выполнения. Будет true, если все строки, удовлетворяющие условию удалены успешно,
|
||||
* иначе false.
|
||||
*/
|
||||
public function Delete (?callable $wherePredicate = null): bool
|
||||
{
|
||||
// Задаю результат
|
||||
$result = true;
|
||||
|
||||
// Получаю массив строк
|
||||
$array = $this->GetRows($wherePredicate);
|
||||
|
||||
// Каждый элемент в этих строках
|
||||
foreach ($array as $item) {
|
||||
// - получаю его индекс в основном массиве
|
||||
$key = array_search($item, $this->Container);
|
||||
|
||||
// - и если индекс найден
|
||||
if ($key !== false) {
|
||||
// -- удаляю элемент из основного массива
|
||||
$resultArray = array_splice($this->Container, $key, 1);
|
||||
|
||||
// -- и если удаление прошло успешно, то будет true
|
||||
$result = $result && count($resultArray) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Возвращаю результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, существует ли объект в массиве.
|
||||
*
|
||||
* @param callable|null $where Условие выборки <code>fn (mixed $item): bool</code>, которое проверяет, подходит ли
|
||||
* объект или нет. Может принимать вместо замыкания null, тогда вернёт false. По умолчанию null.
|
||||
*
|
||||
* @return bool Результат проверки
|
||||
*/
|
||||
public function IsExist (?callable $where = null): bool
|
||||
{
|
||||
// Если условие не задано
|
||||
if ($where === null || !is_callable($where))
|
||||
// - то возвращаем false
|
||||
return false;
|
||||
|
||||
// Вывожу результат
|
||||
return $this->Count($where) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Считает количество объектов в массиве, удовлетворяющих условию.
|
||||
*
|
||||
* @param callable|null $predicate Функция, возвращающая bool: true, если объект удовлетворяет условию, false, если
|
||||
* не удовлетворяет. Параметром является объект массива. Вместо функции можно передать null, тогда результатом
|
||||
* будет общее количество объектов в массиве. По умолчанию, null.
|
||||
*
|
||||
* @return int Количество объектов, удовлетворяющих условию
|
||||
*/
|
||||
public function Count (?callable $predicate = null): int
|
||||
{
|
||||
// Если условие не задано
|
||||
if ($predicate === null || !is_callable($predicate))
|
||||
// - то возвращаем общее количество объектов
|
||||
return count($this->Container);
|
||||
|
||||
// Создаём результирующее число
|
||||
$result = 0;
|
||||
|
||||
// Проходим по массиву
|
||||
foreach ($this->Container as $item) {
|
||||
// - пропускаем не объекты
|
||||
if (!is_object($item))
|
||||
continue;
|
||||
|
||||
// - если элемент удовлетворяет условию
|
||||
if ($predicate($item))
|
||||
// -- добавляем счётчик
|
||||
$result++;
|
||||
}
|
||||
|
||||
// Возвращаем результат
|
||||
return $result;
|
||||
}
|
||||
}
|
129
sources/traits/ObjectArray/ObjectArraySearchAndSortTrait.php
Normal file
129
sources/traits/ObjectArray/ObjectArraySearchAndSortTrait.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace goodboyalex\php_components_pack\traits\ObjectArray;
|
||||
|
||||
/**
|
||||
* Часть кода класса ObjectArray, отвечающая за поиск и исключение объектов.
|
||||
*
|
||||
* @author Александр Бабаев
|
||||
* @package php_components_pack
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
trait ObjectArraySearchAndSortTrait
|
||||
{
|
||||
/**
|
||||
* Сортирует массив объектов, по пользовательской функции.
|
||||
*
|
||||
* @param callable $objectPropertyValuePredicate Функция <code>fn (mixed $item): mixed => $item-><PROPERTY></code>,
|
||||
* возвращающая сравниваемый объект. Возвращаемое значение должно быть значением свойства этого объекта.
|
||||
*
|
||||
* <code>
|
||||
* class Demo;
|
||||
* {
|
||||
* public string $Property1;
|
||||
* public string $Property2;
|
||||
* public function __construct(string $Property1, string $Property2)
|
||||
* {
|
||||
* $this->Property1 = $Property1;
|
||||
* $this->Property2 = $Property2;
|
||||
* }
|
||||
* }
|
||||
* $array = new ObjectArray (
|
||||
* [
|
||||
* new Demo('1', '2'),
|
||||
* new Demo('5', '6'),
|
||||
* new Demo('3', '4')
|
||||
* ]);
|
||||
*
|
||||
* // output:
|
||||
* // [Demo('1', '2'), Demo('3', '4'), Demo('5', '6')]
|
||||
* $result = $array->SortCallback($array, fn ($item): mixed => $item->Property1);
|
||||
* </code>
|
||||
* @param bool $descending Направление сортировки
|
||||
*
|
||||
* @return array Отсортированный массив объектов
|
||||
*/
|
||||
public function SortCallback (callable $objectPropertyValuePredicate, bool $descending = false): array
|
||||
{
|
||||
// Создаём результирующий массив
|
||||
$result = array_merge($this->Container, []);
|
||||
|
||||
// Сортируем массив
|
||||
usort($result,
|
||||
fn ($a, $b)
|
||||
=> !$descending
|
||||
? $objectPropertyValuePredicate($a) <=> $objectPropertyValuePredicate($b)
|
||||
: $objectPropertyValuePredicate($b) <=> $objectPropertyValuePredicate($a));
|
||||
|
||||
// Возвращаем результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сортирует массив объектов, по значению свойства объекта.
|
||||
*
|
||||
* @param string $objectProperty Имя свойства объекта
|
||||
* @param bool $descending Направление сортировки
|
||||
*
|
||||
* @return array Отсортированный массив объектов
|
||||
*/
|
||||
public function Sort (string $objectProperty, bool $descending = false): array
|
||||
{
|
||||
// Создаём результирующий массив
|
||||
$result = array_merge($this->Container, []);
|
||||
|
||||
// Сортируем массив
|
||||
usort($result,
|
||||
fn ($a, $b)
|
||||
=> !$descending
|
||||
? $a->$objectProperty <=> $b->$objectProperty
|
||||
: $b->$objectProperty <=> $a->$objectProperty);
|
||||
|
||||
// Возвращаем результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск объекта в массиве объектов по значению свойства объекта.
|
||||
*
|
||||
* @param string $forProperty Имя искомого свойства объекта.
|
||||
* @param mixed $searchValue Искомое значение свойства объекта.
|
||||
* @param callable|null $compareFunction Функция сравнения, принимающая два параметра: mixed $currentPropertyValue
|
||||
* (текущее значение свойства) и mixed $searchPropertyValue (поисковое значение свойства) и возвращает bool -
|
||||
* true, если значение свойства совпадает с искомым, false - если не совпадает. Если передаётся null, то будет
|
||||
* использоваться DefaultComparerFunction. По умолчанию, null.
|
||||
*
|
||||
* @return mixed Объект, найденный по значению свойства или null, если не найден
|
||||
*/
|
||||
public function Search (string $forProperty, mixed $searchValue, ?callable $compareFunction = null): mixed
|
||||
{
|
||||
// Проходим по массиву
|
||||
foreach ($this->Container as $item) {
|
||||
// - пропускаем не объекты
|
||||
if (!is_object($item))
|
||||
continue;
|
||||
|
||||
// - пропускаем, если значение свойства нет в этом объекте
|
||||
if (!property_exists($item, $forProperty))
|
||||
continue;
|
||||
|
||||
// - пропускаем, если значение свойства не задано
|
||||
if (!isset($item->$forProperty))
|
||||
continue;
|
||||
|
||||
// - если не задана функция сравнения
|
||||
if (is_null($compareFunction) || !is_callable($compareFunction))
|
||||
// -- то устанавливаем функцию сравнения по умолчанию
|
||||
$compareFunction = $this->DefaultComparerFunction;
|
||||
|
||||
// - если значение свойства совпадает с искомым
|
||||
if ($compareFunction($item->$forProperty, $searchValue))
|
||||
// -- возвращаем объект
|
||||
return $item;
|
||||
}
|
||||
|
||||
// Если мы сюда дошли, значить объект не найден - возвращаем null
|
||||
return null;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user