diff --git a/sources/classes/JsonReWriter.php b/sources/classes/JsonReWriter.php index bf406a9..49e7011 100644 --- a/sources/classes/JsonReWriter.php +++ b/sources/classes/JsonReWriter.php @@ -2,8 +2,10 @@ namespace goodboyalex\php_components_pack\classes; +use goodboyalex\php_components_pack\enums\JsonErrorCode; use goodboyalex\php_components_pack\exceptions\JsonException; use goodboyalex\php_components_pack\traits\JsonReWriter\JsonReWriterReadTrait; +use goodboyalex\php_components_pack\traits\JsonReWriter\JsonReWriterWriteTrait; /** * Класс для работы с JSON-файлами. @@ -15,10 +17,37 @@ use goodboyalex\php_components_pack\traits\JsonReWriter\JsonReWriterReadTrait; */ final class JsonReWriter { + /** + * @var string $JsonString Строка JSON. + */ + public string $JsonString { + get { + // Преобразую данные в JSON + $json = json_encode($this->JsonData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + + // Проверка на ошибки + if (json_last_error() !== JSON_ERROR_NONE) + // - если есть ошибки, выбрасываем исключение + throw new JsonException(null, JsonErrorCode::FromLastError(), json_last_error_msg()); + + // Возвращаем JSON + return $json; + } + set { + // Чтение содержимого файла и преобразование JSON в объект + $this->JsonData = json_decode($value, true); + + // Проверка на ошибки + if ($this->JsonData === null && json_last_error() !== JSON_ERROR_NONE) + // - если есть ошибки, выбрасываем исключение + throw new JsonException($value, JsonErrorCode::FromLastError(), json_last_error_msg()); + } + } + /** * @var array $JsonData Массив данных. */ - public array $JsonData; + private array $JsonData; /** * Конструктор класса. @@ -41,18 +70,12 @@ final class JsonReWriter * * @param string $fileName Имя файла. * - * @return void - * @throws JsonException Если файл не может быть сохранен. + * @return bool Сохранены ли данные в файл: true - да, false - нет. */ - public function SaveToFile (string $fileName): void + public function SaveToFile (string $fileName): bool { // Запись данных в файл - file_put_contents($fileName, json_encode($this->JsonData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); - - // Проверка на ошибки - if (json_last_error() !== JSON_ERROR_NONE) - // - если есть ошибки, выбрасываем исключение - throw new JsonException($fileName, json_last_error(), json_last_error_msg()); + return file_put_contents($fileName, $this->JsonString) !== false; } /** @@ -60,23 +83,28 @@ final class JsonReWriter * * @param string $fileName Имя файла. * - * @return void - * @throws JsonException Если файл не существует или содержит ошибки. + * @return bool Загрузились ли данные из файла: true - да, false - нет. */ - public function LoadFromFile (string $fileName): void + public function LoadFromFile (string $fileName): bool { // Проверка существования файла if (!is_file($fileName)) - // - нет? Выбрасываем исключение - throw new JsonException($fileName, -1, "File does not exist / Файл не существует"); + // - если нет, возвращаем false + return false; - // Чтение содержимого файла и преобразование JSON в объект - $this->JsonData = json_decode(file_get_contents($fileName), true); + // Чтение содержимого файла + $result = file_get_contents($fileName); // Проверка на ошибки - if ($this->JsonData === null && json_last_error() !== JSON_ERROR_NONE) - // - если есть ошибки, выбрасываем исключение - throw new JsonException($fileName, json_last_error(), json_last_error_msg()); + if ($result === false) + // - если есть ошибки, возвращаем false + return false; + + // Записываем результат + $this->JsonString = $result; + + // Возвращаем true, если все хорошо + return true; } /** @@ -132,32 +160,12 @@ final class JsonReWriter */ private function CreateKey (string $key, mixed $value): void { - // Получаем массив ключей по вложенности - $keys = $this->ParseKey($key); - // Получаем текущий массив данных - $current = &$this->JsonData; - - // Для каждого ключа до предпоследнего - for ($i = 0; $i < count($keys) - 1; $i++) { - // - проверяем наличие ключа в текущем массиве - if (!isset($current[$keys[$i]])) - // - нет? Создаем ключ (массив) - $current[$keys[$i]] = []; - - // - проверяем, что ключ является массивом - if (!is_array($current[$keys[$i]])) - // - нет? Выбрасываем исключение - throw new JsonException(null, -1, "Key is not an array / Ключ не является массивом!"); - - // - переходим ко вложенному массиву - $current = &$current[$keys[$i]]; - } - - // Создаем ключ (значение) - $current[$keys[count($keys) - 1]] = $value; } - + // Подключаем методы чтения JSON use JsonReWriterReadTrait; + + // Подключаем методы записи JSON + use JsonReWriterWriteTrait; } \ No newline at end of file diff --git a/sources/enums/JsonErrorCode.php b/sources/enums/JsonErrorCode.php new file mode 100644 index 0000000..88f7ea9 --- /dev/null +++ b/sources/enums/JsonErrorCode.php @@ -0,0 +1,108 @@ + 10) + // - верну неизвестную ошибку, если код не в диапазоне + return self::Unknown; + + // Перевожу код в Enum + return self::FromInt($error); + } +} \ No newline at end of file diff --git a/sources/exceptions/JsonException.php b/sources/exceptions/JsonException.php index b3b3109..9831dc8 100644 --- a/sources/exceptions/JsonException.php +++ b/sources/exceptions/JsonException.php @@ -3,6 +3,7 @@ namespace goodboyalex\php_components_pack\exceptions; use Exception; +use goodboyalex\php_components_pack\enums\JsonErrorCode; /** * Ошибка работы с JSON. @@ -15,33 +16,32 @@ use Exception; final class JsonException extends Exception { /** - * @var string|null $JsonFileName Имя файла JSON. + * @var string|null $JsonString Строка JSON. */ - public ?string $JsonFileName; + public ?string $JsonString; /** - * @link https://www.php.net/manual/ru/function.json-last-error.php - * @var int $JsonErrorCode Код ошибки JSON. + * @var JsonErrorCode $ErrorCode Код ошибки JSON. */ - public int $JsonErrorCode; + public JsonErrorCode $ErrorCode; /** * @link https://www.php.net/manual/ru/function.json-last-error-msg.php - * @var string|null $JsonErrorMessage Сообщение об ошибке JSON. + * @var string|null $ErrorMessage Сообщение об ошибке JSON. * * Внимание! В отличие от функции json_last_error_msg(), данная переменная при отсутствии ошибок выводит null, а не * "No error". */ - public ?string $JsonErrorMessage; + public ?string $ErrorMessage; /** * Конструктор. * - * @param string|null $fileName Имя файла JSON. - * @param int $errorCode Код ошибки JSON. + * @param string|null $json Строка JSON. + * @param JsonErrorCode $errorCode Код ошибки JSON. * @param string|null $errorMessage Сообщение об ошибке JSON. */ - public function __construct (?string $fileName = null, int $errorCode = JSON_ERROR_NONE, + public function __construct (?string $json = null, JsonErrorCode $errorCode = JsonErrorCode::Unknown, ?string $errorMessage = null) { // Если код ошибки JSON равен 0 @@ -50,7 +50,7 @@ final class JsonException extends Exception $errorMessage = null; // Сохраняем сообщение об ошибке - $this->JsonErrorMessage = $errorMessage; + $this->ErrorMessage = $errorMessage; // Если сообщение пусто, то присваиваем ему "" для совместимости $errorMessage = $errorMessage ?? ""; @@ -59,9 +59,9 @@ final class JsonException extends Exception parent::__construct($errorMessage); // Присваиваем имя файла - $this->JsonFileName = $fileName; + $this->JsonString = $json; // Присваиваем код ошибки - $this->JsonErrorCode = $errorCode; + $this->ErrorCode = $errorCode; } } \ No newline at end of file diff --git a/sources/traits/JsonReWriter/JsonReWriterReadTrait.php b/sources/traits/JsonReWriter/JsonReWriterReadTrait.php index 20a5b5d..5590b5e 100644 --- a/sources/traits/JsonReWriter/JsonReWriterReadTrait.php +++ b/sources/traits/JsonReWriter/JsonReWriterReadTrait.php @@ -2,6 +2,10 @@ namespace goodboyalex\php_components_pack\traits\JsonReWriter; +use goodboyalex\php_components_pack\enums\JsonErrorCode; +use goodboyalex\php_components_pack\exceptions\JsonException; +use goodboyalex\php_components_pack\interfaces\ISerializable; + /** * Часть кода класса JsonReWriter, отвечающая за методы чтения ключей и значений JSON. * @@ -35,9 +39,12 @@ trait JsonReWriterReadTrait */ public function Read (string $key, mixed $default = null): mixed { + // Проверяем, существует ли ключ if (!$this->IsKeyExists($key)) + // - если нет, то возвращаем значение по умолчанию return $default; + // Разбиваем ключ на массив ключей $keys = $this->ParseKey($key); // Получаем текущий массив данных @@ -104,6 +111,36 @@ trait JsonReWriterReadTrait return (object)$this->Read($key, $default); } + /** + * Читает значение ключа JSON как объект, реализующий интерфейс ISerializable. + * + * @param string $key Ключ JSON. + * @param string $serializableClassName Имя класса, реализующего интерфейс ISerializable, с namespace. + * + * @return ISerializable Инициализированный объект + * @throws JsonException Если класс не реализует интерфейс ISerializable + */ + public function ReadSerializable (string $key, string $serializableClassName): ISerializable + { + // Создаем объект + $instance = new $serializableClassName(); + + // Проверяем, что он реализует интерфейс ISerializable + if (!$instance instanceof ISerializable) + // - если нет, то выбрасываем исключение + throw new JsonException(errorCode: JsonErrorCode::NotISerializable, + errorMessage: "Class $serializableClassName is not implements ISerializable interface / Класс $serializableClassName не реализует интерфейс ISerializable"); + + // Получаем строку JSON из ключа + $json = $this->ReadString($key, $instance->Serialize()); + + // Десериализуем строку JSON в объект + $instance->UnSerialize($json); + + // Возвращаем объект + return $instance; + } + /** * Читает значение ключа JSON как строку. * diff --git a/sources/traits/JsonReWriter/JsonReWriterWriteTrait.php b/sources/traits/JsonReWriter/JsonReWriterWriteTrait.php new file mode 100644 index 0000000..6d2f206 --- /dev/null +++ b/sources/traits/JsonReWriter/JsonReWriterWriteTrait.php @@ -0,0 +1,88 @@ +Write($key, json_encode($value)); + } + + /** + * Записывает значение в ключ JSON. + * + * @param string $key Ключ JSON. + * @param mixed $value Записываемое значение. + * + * @return void + * @throws JsonException Ключ не содержит вложений, хотя от него требуется обратное. + */ + public function Write (string $key, mixed $value): void + { + // Получаем массив ключей по вложенности + $keys = $this->ParseKey($key); + + // Получаем текущий массив данных + $current = &$this->JsonData; + + // Для каждого ключа до предпоследнего + for ($i = 0; $i < count($keys) - 1; $i++) { + // - проверяем наличие ключа в текущем массиве + if (!isset($current[$keys[$i]])) + // - нет? Создаем ключ (массив) + $current[$keys[$i]] = []; + + // - проверяем, что ключ является массивом + if (!is_array($current[$keys[$i]])) + // - нет? Выбрасываем исключение + throw new JsonException(errorCode: JsonErrorCode::KeyIsNotArray, + errorMessage: "The key does not contain attachments, although it is required to do the opposite / Ключ не содержит вложений, хотя от него требуется обратное!"); + + // - переходим ко вложенному массиву + $current = &$current[$keys[$i]]; + } + + // Создаем ключ (значение) + $current[$keys[count($keys) - 1]] = $value; + } + + /** + * Записывает объект, реализующий интерфейс ISerializable, в ключ JSON. + * + * @param string $key Ключ JSON. + * @param ISerializable $serializableValue Записываемый объект, реализующий интерфейс ISerializable. + * + * @return void + * @throws JsonException Ключ не содержит вложений, хотя от него требуется обратное. + */ + public function WriteSerializable (string $key, ISerializable $serializableValue): void + { + // Сериализуем объект + $serialized = $serializableValue->Serialize(); + + // Записываем в ключ + $this->Write($key, $serialized); + } +} \ No newline at end of file diff --git a/tests/classes/JsonReWriterTest.php b/tests/classes/JsonReWriterTest.php index 7cd5591..f415271 100644 --- a/tests/classes/JsonReWriterTest.php +++ b/tests/classes/JsonReWriterTest.php @@ -12,11 +12,8 @@ class JsonReWriterTest extends TestCase $this->PrepareForTest(); $json = new JsonReWriter(); - $json->CreateKey("test/subtest/AAA", "123"); - - var_dump($json->JsonData); - - //$json->SaveToFile(__DIR__ . '/test.json'); + $json->Write("test/subtest/AAA", "123"); + $json->SaveToFile(__DIR__ . '/test.json'); } private function PrepareForTest (): void @@ -29,4 +26,4 @@ class JsonReWriterTest extends TestCase { } -} +} \ No newline at end of file