4 Commits

Author SHA1 Message Date
697f6b81b9 20250424
[Д] [ObjectArray->Add] Функция добавляет объект в массив объектов, хранящийся в данном классе (аналогично добавлению элемента в массив с помощью []).

[Д] [ObjectArray->AddRange] Функция добавляет массив объектов (или объекты, заданные с помощью array) в массив объектов, хранящийся в данном классе.

[И] [ObjectArray->Update] Добавление с помощью foreach заменено на AddRange.

[Д] [IDuplicated] Добавлен интерфейс реализации дублирования классов.

[Д] [IStoredAtSQL] Добавлен интерфейс поддержки моделей и классов, реализующих хранение свойств в SQL базе данных.
2025-04-24 07:31:59 +03:00
8da3c1df38 20250224
[Д] [StringExtension::Replace] Функция заменяет все вхождения строки поиска на строку замены в заданной строке (аналог mb_str_replace).

[Д] [StringExtension::ReplaceAll] Функция заменяет все вхождения строк поиск на соответствующие строки замены в заданной строке.
2025-02-24 23:44:57 +03:00
07a994df83 20250223-1
[О] [ClassMapper::MapToClassProperty] и [ClassMapper::SetParameterToClass]: Исправлена ошибка, при которой некорректно переводился тип bool.
2025-02-23 14:05:30 +03:00
e09ea26a3c 20250223
[О] [ClassMapper::MapClass]: Отменена проверка свойства на доступность get и set, так как выдавала ошибку. Используйте лучше в таких случаях $options['ignored'] для таких свойств.
2025-02-23 11:34:19 +03:00
9 changed files with 311 additions and 140 deletions

View File

@@ -4,12 +4,10 @@ namespace goodboyalex\php_components_pack\classes;
use DateTimeImmutable;
use DateTimeInterface;
use Error;
use Exception;
use ReflectionClass;
use ReflectionException;
use stdClass;
use Throwable;
use UnitEnum;
/**
@@ -51,79 +49,21 @@ final class ClassMapper
// ---- то исключаю его из массива разрешенных
unset($options['allowed'][array_search($ignoredProperty, $options['allowed'])]);
// Задаю массив свойств
$properties = [];
// Получаю имя исходного класса
$className = get_class($from);
// Получение всех свойств класса
try {
$reflection = new ReflectionClass($className);
}
catch (ReflectionException) {
return;
}
// Получение всех свойств класса
$props = $reflection->getProperties();
// Создаю экземпляр класса
$instance = new $className();
// Для каждого свойства
foreach ($props as $prop) {
// - получаю имя свойства
$propName = $prop->getName();
// - получаю значение свойства
$value = $from->$propName;
try {
// - получаю тип свойства
$typeOf = gettype($from->$propName);
// - получаю значение свойства по типу и по умолчанию
$writeValue = self::GetDefaults($typeOf);
try {
// - проверяем, можно ли записать и прочитать значение
// -- пытаюсь установить значение
$instance->$propName = $writeValue;
// -- пытаюсь прочитать установленное значение
$readValue = $instance->$propName;
// -- и проверяю, что значение совпадают
/** @noinspection PhpConditionAlreadyCheckedInspection */
if ($readValue !== $writeValue)
continue;
}
catch (Throwable) {
// - в случае ошибки, понимаем, что свойство доступно или только для чтения, или
// только для записи и оно нам не подходит. Поэтому пропускаем его.
continue;
}
// - если свойство игнорируется
if (in_array($propName, $options['ignored']))
// -- пропускаю
continue;
// - если свойство не разрешено
if (count($options['allowed']) > 0 && !in_array($propName, $options['allowed']))
// -- пропускаю
continue;
// Если не было ошибки, значит свойство имеет и геттер, и сеттер
$properties[$propName] = $value;
}
catch (Error) {
// - в случае ошибки, понимаем, что свойство нам не подходит. Поэтому пропускаю его.
continue;
}
}
// Получаю массив свойств
$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))
// -- то присваиваю значение
@@ -131,26 +71,6 @@ final class ClassMapper
}
}
/**
* Возвращает значение по умолчанию для типа $typeName.
*
* @param string $typeName Тип
*
* @return mixed Значение по умолчанию
*/
public static function GetDefaults (string $typeName): mixed
{
return match (strtolower($typeName)) {
'int', 'integer' => 0,
'float', 'double' => 0.0,
'bool', 'boolean' => false,
'string' => '',
'array' => [],
'object' => new stdClass(),
default => null,
};
}
/**
* Подготавливает значения свойств класса.
*
@@ -160,8 +80,7 @@ final class ClassMapper
*
* @return array Массив данных класса.
*/
public
static function PrepareClassProperties (array $params, ReflectionClass $classReflector,
public static function PrepareClassProperties (array $params, ReflectionClass $classReflector,
array $options = self::DefaultOptions): array
{
// Создаю массив данных класса
@@ -221,8 +140,7 @@ final class ClassMapper
*
* @return void
*/
public
static function GetClassParametersArrayParser (array &$source, array $parametersKeys, mixed $value,
public static function GetClassParametersArrayParser (array &$source, array $parametersKeys, mixed $value,
array $options = self::DefaultOptions): void
{
// Если массив имен свойств пустой
@@ -271,8 +189,7 @@ final class ClassMapper
* @return mixed Объект класса
* @throws Exception
*/
public
static function MapToClassProperty (string $className, array $properties): mixed
public static function MapToClassProperty (string $className, array $properties): mixed
{
// Создаю
try {
@@ -294,6 +211,15 @@ final class ClassMapper
// -- тип свойства
$propertyType = $property->getType();
// - если значение является типом bool
if ($propertyType->getName() === 'bool') {
// -- присваиваю дату
self::SetParameterToClass($classReflector, $key, $classObj, $value == "1");
// -- следующий элемент
continue;
}
// - если значение является классом
if (!$propertyType->isBuiltin() && is_array($value)) {
// -- присваиваю объект
@@ -368,8 +294,7 @@ final class ClassMapper
*
* @throws Exception
*/
public
static function SetParameterToClass (ReflectionClass $classReflector, string $propertyName,
public static function SetParameterToClass (ReflectionClass $classReflector, string $propertyName,
mixed $classObj, mixed $value): void
{
try {
@@ -384,7 +309,7 @@ final class ClassMapper
$property->setAccessible(true);
// Если значение null
if ($value == null || $value == "null")
if (!is_bool($value) && ($value == null || $value == "null"))
// - то присваиваю значение по умолчанию
$value = self::GetDefaults($property->getType()->getName());
@@ -396,4 +321,24 @@ final class ClassMapper
throw new Exception($exception->getMessage());
}
}
/**
* Возвращает значение по умолчанию для типа $typeName.
*
* @param string $typeName Тип
*
* @return mixed Значение по умолчанию
*/
public static function GetDefaults (string $typeName): mixed
{
return match (strtolower($typeName)) {
'int', 'integer' => 0,
'float', 'double' => 0.0,
'bool', 'boolean' => false,
'string' => '',
'array' => [],
'object' => new stdClass(),
default => null,
};
}
}

View File

@@ -9,6 +9,7 @@ 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 goodboyalex\php_components_pack\traits\ObjectArray\ObjectArraySpecialTrait;
use IteratorAggregate;
/**
@@ -38,6 +39,9 @@ final class ObjectArray implements ArrayAccess, IteratorAggregate, Countable, IS
// LINQ-подобные методы
use ObjectArrayLINQTrait;
// Специальные методы
use ObjectArraySpecialTrait;
/**
* Конструктор класса.
*
@@ -47,40 +51,4 @@ final class ObjectArray implements ArrayAccess, IteratorAggregate, Countable, IS
{
$this->Container = $array;
}
/**
* Возвращает массив объектов, хранящихся в данном классе.
*
* @return array Массив объектов, хранящихся в данном классе.
*/
public function ToArray (): array
{
return $this->Container;
}
/**
* Очищает массив объектов, хранящийся в данном классе.
*
* @return void
*/
public function Clear (): void
{
// Очищаем массив
unset($this->Container);
// Создаем новый массив
$this->Container = [];
}
/**
* Объединяет массив объектов, хранящийся в данном классе, с массивом объектов, переданным в качестве параметра.
*
* @param array $objects Массив объектов, который будет объединен с массивом объектов, хранящимся в данном классе.
*
* @return void
*/
public function Merge (array $objects): void
{
$this->Container = array_merge($this->Container, $objects);
}
}

View File

@@ -207,4 +207,63 @@ final class StringExtension
// Возвращаю обрезанный текст
return mb_substr($text, 0, $lengthWithoutEndDots) . $endDots;
}
/**
* Функция заменяет все вхождения строк поиск на соответствующие строки замены в заданной строке.
*
* @param array $searchReplace Массив с парами поиска и замены. Например, <code>['-' => '#', '$' => '%']</code>
* заменит все дефисы на # и все доллары на %.
* @param string $subject Строка, в которой нужно выполнить замену.
* @param string $encoding (необязательный) Кодировка (по умолчанию, UTF-8).
*
* @return string Возвращает новую строку с выполненными заменами.
*/
public static function ReplaceAll (array $searchReplace, string $subject, string $encoding = 'UTF-8'): string
{
// Создаю результат
$result = $subject;
// Для каждой пары поиска и замены
foreach ($searchReplace as $search => $replace)
// - заменяю все вхождения строки поиска на строку замены в заданной строке
$result = self::Replace($search, $replace, $result, $encoding);
// Возвращаю результат
return $result;
}
/**
* Функция заменяет все вхождения строки поиска на строку замены в заданной строке (аналог mb_str_replace).
*
* @param string $search Строка, которую нужно найти и заменить.
* @param string $replace Строка, на которую нужно заменить найденную строку.
* @param string $subject Строка, в которой нужно выполнить замену.
* @param string $encoding (необязательный) Кодировка (по умолчанию, UTF-8).
*
* @return string Возвращает новую строку с выполненной заменой.
*/
public static function Replace (string $search, string $replace, string $subject,
string $encoding = 'UTF-8'): string
{
// Если кодировка не UTF-8
if ($encoding != 'UTF-8') {
// - то перекодируем строку поиска, замены и исходную строку в UTF-8
$search = mb_convert_encoding($search, 'UTF-8', $encoding);
// - и перекодируем строку замены в UTF-8
$replace = mb_convert_encoding($replace, 'UTF-8', $encoding);
// - и перекодируем исходную строку в UTF-8
$subject = mb_convert_encoding($subject, 'UTF-8', $encoding);
}
// С помощью регулярного выражения заменяем все вхождения строки поиска на строку замены
$result = preg_replace('/' . preg_quote($search, '/') . '/u', $replace, $subject);
// Если кодировка не UTF-8
if ($encoding != 'UTF-8')
// - то перекодируем результат в исходную кодировку
$result = mb_convert_encoding($result, $encoding, 'UTF-8');
// Возвращаем результат
return $result;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace goodboyalex\php_components_pack\interfaces;
/**
* Интерфейс реализации дублирования классов.
*
* @author Александр Бабаев
* @package freecms
* @version 0.1
* @since 0.1
*/
interface IDuplicated
{
/**
* Дублирование класса.
*
* @return object Класс-дублёр.
*/
public function Duplicate (): object;
}

View File

@@ -0,0 +1,32 @@
<?php
namespace goodboyalex\php_components_pack\interfaces;
/**
* Интерфейс поддержки моделей и классов, реализующих хранение свойств в SQL базе данных.
*
* @author Александр Бабаев
* @package freecms
* @version 0.1
* @since 0.1
*/
interface IStoredAtSQL
{
/**
* Вывод параметров добавления/обновления данных в базу данных.
*
* @param bool $withId Нужно ли добавлять Id в массив
*
* @return array Массив параметров
*/
public function ToSQL (bool $withId = true): array;
/**
* Преобразование данных из базы данных в модель, класс.
*
* @param array $sqlData Данные из базы данных
*
* @return self Класс модели с заполненными данными из базы данных
*/
public function FromSQL (array $sqlData): self;
}

View File

@@ -196,8 +196,7 @@ trait ObjectArrayLINQTrait
$result = $this->Delete($wherePredicate);
// Добавляю новые элементы
foreach ($setItems as $item)
$this->Container[] = $item;
$this->AddRange($setItems);
// Возвращаю результат
return $result;

View File

@@ -0,0 +1,81 @@
<?php
namespace goodboyalex\php_components_pack\traits\ObjectArray;
use goodboyalex\php_components_pack\classes\ObjectArray;
/**
* Часть кода класса ObjectArray, отвечающая за специальные функции.
*
* @author Александр Бабаев
* @package php_components_pack
* @version 1.0
* @since 1.0
*/
trait ObjectArraySpecialTrait
{
/**
* Очищает массив объектов, хранящийся в данном классе.
*
* @return void
*/
public function Clear (): void
{
// Очищаем массив
unset($this->Container);
// Создаем новый массив
$this->Container = [];
}
/**
* Добавляет объект в массив объектов, хранящийся в данном классе (аналогично добавлению элемента в массив с
* помощью []).
*
* @param mixed $object Объект, который будет добавлен в массив объектов, хранящийся в данном классе.
*
* @return void
*/
public function Add (mixed $object): void
{
$this->Container[] = $object;
}
/**
* Добавляет массив объектов (или объекты, заданные с помощью array) в массив объектов, хранящийся в данном классе.
*
* @param ObjectArray|array $objects Массив объектов, которые будут добавлены в массив объектов.
*
* @return void
*/
public function AddRange (ObjectArray|array $objects): void
{
// Если передан массив, то не изменяем его, а если передан объект класса ObjectArray, то конвертируем его в массив объектов
$objectsToAdd = is_array($objects) ? $objects : $objects->ToArray();
// Добавляем массив объектов
$this->Merge($objectsToAdd);
}
/**
* Возвращает массив объектов, хранящихся в данном классе.
*
* @return array Массив объектов, хранящихся в данном классе.
*/
public function ToArray (): array
{
return $this->Container;
}
/**
* Объединяет массив объектов, хранящийся в данном классе, с массивом объектов, переданным в качестве параметра.
*
* @param array $objects Массив объектов, который будет объединен с массивом объектов, хранящимся в данном классе.
*
* @return void
*/
public function Merge (array $objects): void
{
$this->Container = array_merge($this->Container, $objects);
}
}

View File

@@ -43,6 +43,7 @@ class ObjectArrayTest extends TestCase
require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArrayConstantsTrait.php';
require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArrayLINQTrait.php';
require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArraySearchAndSortTrait.php';
require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArraySpecialTrait.php';
require_once __DIR__ . '/../../sources/classes/ObjectArray.php';
}
@@ -430,4 +431,53 @@ class ObjectArrayTest extends TestCase
$this->assertEquals(3, $b_Array->First()->b);
$this->assertEquals(5, $b_Array->Last()->b);
}
public function testAdd ()
{
$this->PrepareForTest();
$obj = new A("a", 3, true);
$a_Array = new ObjectArray([]);
$a_Array->Add($obj);
$b_Array = $a_Array->GetRow(fn (A $a) => $a->a == "a");
$this->assertEquals(3, $b_Array->b);
}
public function testAddRange ()
{
$this->PrepareForTest();
$array = [
new A("a", 3, true),
new A("c", 2, false),
new A("b", 1, true),
new A("d", 5, true),
new A("e", 4, true),
new A("f", 6, false)
];
$a_Array = new ObjectArray();
$a_Array->AddRange($array);
$this->assertEquals(6, $a_Array->Count());
$array2 = [
new A("g", 3, true),
new A("h", 2, false),
new A("i", 1, true),
new A("k", 5, true),
new A("l", 4, true),
new A("m", 6, false)
];
$objectArray = new ObjectArray($array2);
$a_Array->AddRange($objectArray);
$this->assertEquals(12, $a_Array->Count());
}
}

View File

@@ -63,4 +63,20 @@ class StringExtensionTest extends TestCase
$this->assertEquals('', StringExtension::GetShortText('test', 0));
}
public function testReplace ()
{
$this->PrepareForTest();
$this->assertEquals('Все 2 ночи и 2 дня 2 друга искали тебя!',
StringExtension::Replace("4", "2", "Все 4 ночи и 4 дня 2 друга искали тебя!"));
}
public function testReplaceAll ()
{
$this->PrepareForTest();
$this->assertEquals('Все 5 ночи и 5 дня 2 друга искали тебя!',
StringExtension::ReplaceAll(["4" => "5", "3" => "2"], "Все 4 ночи и 4 дня 3 друга искали тебя!"));
}
}