From 82085f26fb12d6be2c986c33906bdefa21484aaf Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 18 Nov 2023 14:51:09 +0900 Subject: [PATCH 01/38] feat: add Database\DataConverter class --- .../Database/DataConverter/Cast/ArrayCast.php | 44 ++ .../Database/DataConverter/Cast/BaseCast.php | 44 ++ .../DataConverter/Cast/BooleanCast.php | 36 ++ .../Database/DataConverter/Cast/CSVCast.php | 44 ++ .../DataConverter/Cast/CastInterface.php | 38 ++ .../DataConverter/Cast/DatetimeCast.php | 49 ++ .../Database/DataConverter/Cast/FloatCast.php | 28 ++ .../DataConverter/Cast/IntBoolCast.php | 44 ++ .../DataConverter/Cast/IntegerCast.php | 32 ++ .../Database/DataConverter/Cast/JsonCast.php | 67 +++ .../DataConverter/Cast/TimestampCast.php | 46 ++ .../Database/DataConverter/Cast/URICast.php | 46 ++ .../Database/DataConverter/DataConverter.php | 173 +++++++ .../Exceptions/CastException.php | 21 + .../DataConverter/DataConverterTest.php | 468 ++++++++++++++++++ 15 files changed, 1180 insertions(+) create mode 100644 system/Database/DataConverter/Cast/ArrayCast.php create mode 100644 system/Database/DataConverter/Cast/BaseCast.php create mode 100644 system/Database/DataConverter/Cast/BooleanCast.php create mode 100644 system/Database/DataConverter/Cast/CSVCast.php create mode 100644 system/Database/DataConverter/Cast/CastInterface.php create mode 100644 system/Database/DataConverter/Cast/DatetimeCast.php create mode 100644 system/Database/DataConverter/Cast/FloatCast.php create mode 100644 system/Database/DataConverter/Cast/IntBoolCast.php create mode 100644 system/Database/DataConverter/Cast/IntegerCast.php create mode 100644 system/Database/DataConverter/Cast/JsonCast.php create mode 100644 system/Database/DataConverter/Cast/TimestampCast.php create mode 100644 system/Database/DataConverter/Cast/URICast.php create mode 100644 system/Database/DataConverter/DataConverter.php create mode 100644 system/Database/DataConverter/Exceptions/CastException.php create mode 100644 tests/system/Database/DataConverter/DataConverterTest.php diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php new file mode 100644 index 000000000000..4b41d9367d49 --- /dev/null +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Class ArrayCast + * + * DB column: string <--> PHP: array + */ +class ArrayCast extends BaseCast implements CastInterface +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): array + { + if (! is_string($value)) { + self::invalidTypeValueError($value); + } + + if ((strpos($value, 'a:') === 0 || strpos($value, 's:') === 0)) { + $value = unserialize($value, ['allowed_classes' => false]); + } + + return (array) $value; + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []): string + { + return serialize($value); + } +} diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php new file mode 100644 index 000000000000..d35b761622f5 --- /dev/null +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +use TypeError; + +/** + * Class BaseCast + */ +abstract class BaseCast implements CastInterface +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []) + { + return $value; + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []) + { + return $value; + } + + /** + * Throws TypeError + */ + protected static function invalidTypeValueError(mixed $value): never + { + throw new TypeError('Invalid type value: ' . var_export($value, true)); + } +} diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/Database/DataConverter/Cast/BooleanCast.php new file mode 100644 index 000000000000..2f7a50eca800 --- /dev/null +++ b/system/Database/DataConverter/Cast/BooleanCast.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Class BooleanCast + * + * DB column: bool|int(0/1) <--> PHP: bool + */ +class BooleanCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): bool + { + // For PostgreSQL + if ($value === 't') { + return true; + } + if ($value === 'f') { + return false; + } + + return filter_var($value, FILTER_VALIDATE_BOOLEAN); + } +} diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php new file mode 100644 index 000000000000..b1af9ea0597f --- /dev/null +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Class CSVCast + * + * DB column: string <--> PHP: array + */ +class CSVCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): array + { + if (! is_string($value)) { + self::invalidTypeValueError($value); + } + + return explode(',', $value); + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []): string + { + if (! is_array($value)) { + self::invalidTypeValueError($value); + } + + return implode(',', $value); + } +} diff --git a/system/Database/DataConverter/Cast/CastInterface.php b/system/Database/DataConverter/Cast/CastInterface.php new file mode 100644 index 000000000000..540623ea3339 --- /dev/null +++ b/system/Database/DataConverter/Cast/CastInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Interface CastInterface + */ +interface CastInterface +{ + /** + * Takes value from database, returns its value for PHP. + * + * @param bool|float|int|string|null $value Data + * @param array $params Additional param + * + * @return array|bool|float|int|object|string|null + */ + public static function fromDatabase($value, array $params = []); + + /** + * Takes the PHP value, returns its value for database. + * + * @param array|bool|float|int|object|string|null $value Data + * @param array $params Additional param + * + * @return bool|float|int|string|null + */ + public static function toDatabase($value, array $params = []); +} diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php new file mode 100644 index 000000000000..d72c96076420 --- /dev/null +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +use CodeIgniter\I18n\Time; +use Exception; + +/** + * Class DatetimeCast + * + * DB column: datetime <--> PHP: Time + */ +class DatetimeCast extends BaseCast +{ + /** + * {@inheritDoc} + * + * @throws Exception + */ + public static function fromDatabase($value, array $params = []): Time + { + if (! is_string($value)) { + self::invalidTypeValueError($value); + } + + return Time::parse($value); + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []): string + { + if (! $value instanceof Time) { + self::invalidTypeValueError($value); + } + + return (string) $value; + } +} diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/Database/DataConverter/Cast/FloatCast.php new file mode 100644 index 000000000000..974b747aa170 --- /dev/null +++ b/system/Database/DataConverter/Cast/FloatCast.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Class FloatCast + * + * DB column: float <--> PHP: float + */ +class FloatCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): float + { + return (float) $value; + } +} diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php new file mode 100644 index 000000000000..4a88e829d4aa --- /dev/null +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Int Bool Cast + * + * DB column: int(0/1) <--> PHP: bool + */ +final class IntBoolCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): bool + { + if (! is_int($value) && ! is_string($value)) { + self::invalidTypeValueError($value); + } + + return (bool) $value; + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []): int + { + if (! is_bool($value)) { + self::invalidTypeValueError($value); + } + + return (int) $value; + } +} diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php new file mode 100644 index 000000000000..14c9862e8152 --- /dev/null +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +/** + * Class IntegerCast + * + * DB column: int <--> PHP: int + */ +class IntegerCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): int + { + if (! is_string($value) && ! is_int($value)) { + self::invalidTypeValueError($value); + } + + return (int) $value; + } +} diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php new file mode 100644 index 000000000000..83c7f66ee7e3 --- /dev/null +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +use CodeIgniter\Database\DataConverter\Exceptions\CastException; +use JsonException; +use stdClass; + +/** + * Class JsonCast + * + * DB column: string <--> PHP: array|stdClass + */ +class JsonCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []) + { + if (! is_string($value)) { + self::invalidTypeValueError($value); + } + + $associative = in_array('array', $params, true); + + $output = ($associative ? [] : new stdClass()); + + if ( + (strlen($value) > 1 && in_array($value[0], ['[', '{', '"'], true)) + || is_numeric($value) + ) { + try { + $output = json_decode($value, $associative, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw CastException::forInvalidJsonFormat($e->getCode()); + } + } + + return $output; + } + + /** + * {@inheritDoc} + * + * @param mixed $value + */ + public static function toDatabase($value, array $params = []): string + { + try { + $output = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw CastException::forInvalidJsonFormat($e->getCode()); + } + + return $output; + } +} diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php new file mode 100644 index 000000000000..e20414bf2cc1 --- /dev/null +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +use CodeIgniter\I18n\Time; + +/** + * Class TimestampCast + * + * DB column: timestamp <--> PHP: Time + */ +class TimestampCast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): Time + { + if (! is_int($value) && ! is_string($value)) { + self::invalidTypeValueError($value); + } + + return Time::createFromTimestamp((int) $value); + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []): int + { + if (! $value instanceof Time) { + self::invalidTypeValueError($value); + } + + return $value->getTimestamp(); + } +} diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php new file mode 100644 index 000000000000..481a089f62cb --- /dev/null +++ b/system/Database/DataConverter/Cast/URICast.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Cast; + +use CodeIgniter\HTTP\URI; + +/** + * Class URICast + * + * DB column: string <--> PHP: URI + */ +class URICast extends BaseCast +{ + /** + * {@inheritDoc} + */ + public static function fromDatabase($value, array $params = []): URI + { + if (! is_string($value)) { + self::invalidTypeValueError($value); + } + + return new URI($value); + } + + /** + * {@inheritDoc} + */ + public static function toDatabase($value, array $params = []): string + { + if (! $value instanceof URI) { + self::invalidTypeValueError($value); + } + + return (string) $value; + } +} diff --git a/system/Database/DataConverter/DataConverter.php b/system/Database/DataConverter/DataConverter.php new file mode 100644 index 000000000000..36b1bc20f7a5 --- /dev/null +++ b/system/Database/DataConverter/DataConverter.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter; + +use CodeIgniter\Database\DataConverter\Cast\ArrayCast; +use CodeIgniter\Database\DataConverter\Cast\BooleanCast; +use CodeIgniter\Database\DataConverter\Cast\CastInterface; +use CodeIgniter\Database\DataConverter\Cast\CSVCast; +use CodeIgniter\Database\DataConverter\Cast\DatetimeCast; +use CodeIgniter\Database\DataConverter\Cast\FloatCast; +use CodeIgniter\Database\DataConverter\Cast\IntBoolCast; +use CodeIgniter\Database\DataConverter\Cast\IntegerCast; +use CodeIgniter\Database\DataConverter\Cast\JsonCast; +use CodeIgniter\Database\DataConverter\Cast\TimestampCast; +use CodeIgniter\Database\DataConverter\Cast\URICast; +use InvalidArgumentException; + +/** + * PHP data <==> DB data converter + * + * @see \CodeIgniter\Database\DataConverter\DataConverterTest + */ +class DataConverter +{ + /** + * Custom convert handlers + * + * @var array> [type => classname] + */ + protected $castHandlers = []; + + /** + * Default convert handlers + * + * @var array> [type => classname] + */ + private array $defaultCastHandlers = [ + 'array' => ArrayCast::class, + 'bool' => BooleanCast::class, + 'boolean' => BooleanCast::class, + 'csv' => CSVCast::class, + 'datetime' => DatetimeCast::class, + 'double' => FloatCast::class, + 'float' => FloatCast::class, + 'int' => IntegerCast::class, + 'integer' => IntegerCast::class, + 'int-bool' => IntBoolCast::class, + 'json' => JsonCast::class, + 'timestamp' => TimestampCast::class, + 'uri' => URICast::class, + ]; + + /** + * @param array $castHandlers Custom convert handlers + */ + public function __construct( + /** + * @var array [column => type] + */ + private array $types, + array $castHandlers = [] + ) { + if ($castHandlers !== []) { + $this->castHandlers = $castHandlers; + } + } + + /** + * Converts data from DB to PHP array with specified type values. + */ + public function fromDatabase(array $dbData): array + { + $output = []; + + foreach ($dbData as $column => $value) { + $output[$column] = $this->castAs($value, $column, 'fromDatabase'); + } + + return $output; + } + + /** + * Converts PHP array to data for DB column types. + */ + public function toDatabase(array $phpData): array + { + $output = []; + + foreach ($phpData as $column => $value) { + $output[$column] = $this->castAs($value, $column, 'toDatabase'); + } + + return $output; + } + + /** + * Provides the ability to cast an item as a specific data type. + * Add ? at the beginning of $type (i.e. ?string) to get `null` + * instead of casting $value if ($value === null). + * + * @param bool|float|int|string|null $value The value to convert + * @param string $column The column name + * @param string $method Allowed to "fromDatabase" and "toDatabase" + * @phpstan-param 'fromDatabase'|'toDatabase' $method + * + * @return array|bool|float|int|object|string|null + */ + protected function castAs($value, string $column, string $method = 'fromDatabase') + { + // If the type is not defined, return as it is. + if (! isset($this->types[$column])) { + return $value; + } + + $type = $this->types[$column]; + + $isNullable = false; + + if (str_starts_with($type, '?')) { + $isNullable = true; + + if ($value === null) { + return null; + } + + $type = substr($type, 1); + } + + // In order not to create a separate handler for the + // json-array type, we transform the required one. + $type = ($type === 'json-array') ? 'json[array]' : $type; + + $params = []; + + // Attempt to retrieve additional parameters if specified + // type[param, param2,param3] + if (preg_match('/\A(.+)\[(.+)\]\z/', $type, $matches)) { + $type = $matches[1]; + $params = array_map('trim', explode(',', $matches[2])); + } + + if ($isNullable) { + $params[] = 'nullable'; + } + + $type = trim($type, '[]'); + + $handlers = array_merge($this->defaultCastHandlers, $this->castHandlers); + + if (! isset($handlers[$type])) { + throw new InvalidArgumentException('No such handler. Invalid type: ' . $type); + } + + $handler = $handlers[$type]; + + if (! is_subclass_of($handler, CastInterface::class)) { + throw new InvalidArgumentException( + 'Invalid class type. It must implement CastInterface. class: ' . $handler + ); + } + + return $handler::$method($value, $params); + } +} diff --git a/system/Database/DataConverter/Exceptions/CastException.php b/system/Database/DataConverter/Exceptions/CastException.php new file mode 100644 index 000000000000..a5576c822c70 --- /dev/null +++ b/system/Database/DataConverter/Exceptions/CastException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter\Exceptions; + +use CodeIgniter\Entity\Exceptions\CastException as EntityCastException; + +/** + * CastException is thrown for invalid cast initialization and management. + */ +class CastException extends EntityCastException +{ +} diff --git a/tests/system/Database/DataConverter/DataConverterTest.php b/tests/system/Database/DataConverter/DataConverterTest.php new file mode 100644 index 000000000000..9815aeccc4f5 --- /dev/null +++ b/tests/system/Database/DataConverter/DataConverterTest.php @@ -0,0 +1,468 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\DataConverter; + +use CodeIgniter\HTTP\URI; +use CodeIgniter\I18n\Time; +use CodeIgniter\Test\CIUnitTestCase; +use InvalidArgumentException; + +/** + * @internal + * + * @group Others + */ +final class DataConverterTest extends CIUnitTestCase +{ + public function testInstantiate(): void + { + $types = [ + 'id' => 'int', + 'remark' => 'json', + ]; + $converter = new DataConverter($types); + + $this->assertInstanceOf(DataConverter::class, $converter); + } + + /** + * @dataProvider provideConvertDataFromDB + */ + public function testConvertDataFromDB(array $types, array $dbData, array $expected): void + { + $converter = new DataConverter($types); + + $data = $converter->fromDatabase($dbData); + + $this->assertSame($expected, $data); + } + + /** + * @dataProvider provideConvertDataToDB + */ + public function testConvertDataToDB(array $types, array $phpData, array $expected): void + { + $converter = new DataConverter($types); + + $data = $converter->toDatabase($phpData); + + $this->assertSame($expected, $data); + } + + public static function provideConvertDataFromDB(): iterable + { + yield from [ + 'int and json-array' => [ + [ + 'id' => 'int', + 'remark' => 'json-array', + ], + [ + 'id' => '1', + 'remark' => '{"foo":"bar","baz":true}', + ], + [ + 'id' => 1, + 'remark' => ['foo' => 'bar', 'baz' => true], + ], + ], + 'nullable null' => [ + [ + 'id' => 'int', + 'remark' => '?json-array', + ], + [ + 'id' => '1', + 'remark' => null, + ], + [ + 'id' => 1, + 'remark' => null, + ], + ], + 'nullable not null' => [ + [ + 'id' => 'int', + 'remark' => '?json-array', + ], + [ + 'id' => '1', + 'remark' => '{"foo":"bar", "baz":true}', + ], + [ + 'id' => 1, + 'remark' => ['foo' => 'bar', 'baz' => true], + ], + ], + 'int-bool' => [ + [ + 'id' => 'int', + 'active' => 'int-bool', + ], + [ + 'id' => '1', + 'active' => '1', + ], + [ + 'id' => 1, + 'active' => true, + ], + ], + 'array' => [ + [ + 'id' => 'int', + 'attr' => 'array', + ], + [ + 'id' => '1', + 'attr' => 'a:1:{s:3:"foo";s:3:"bar";}', + ], + [ + 'id' => 1, + 'attr' => ['foo' => 'bar'], + ], + ], + 'bool 1' => [ + [ + 'id' => 'int', + 'active' => 'bool', + ], + [ + 'id' => '1', + 'active' => '1', + ], + [ + 'id' => 1, + 'active' => true, + ], + ], + 'bool t' => [ + [ + 'id' => 'int', + 'active' => 'bool', + ], + [ + 'id' => '1', + 'active' => 't', + ], + [ + 'id' => 1, + 'active' => true, + ], + ], + 'bool f' => [ + [ + 'id' => 'int', + 'active' => 'bool', + ], + [ + 'id' => '1', + 'active' => 'f', + ], + [ + 'id' => 1, + 'active' => false, + ], + ], + 'csv' => [ + [ + 'id' => 'int', + 'data' => 'csv', + ], + [ + 'id' => '1', + 'data' => 'foo,bar,bam', + ], + [ + 'id' => 1, + 'data' => ['foo', 'bar', 'bam'], + ], + ], + 'float' => [ + [ + 'id' => 'int', + 'temp' => 'float', + ], + [ + 'id' => '1', + 'temp' => '15.9', + ], + [ + 'id' => 1, + 'temp' => 15.9, + ], + ], + ]; + } + + public static function provideConvertDataToDB(): iterable + { + yield from [ + 'int and json-array' => [ + [ + 'id' => 'int', + 'remark' => 'json-array', + ], + [ + 'id' => 1, + 'remark' => ['foo' => 'bar', 'baz' => true], + ], + [ + 'id' => 1, + 'remark' => '{"foo":"bar","baz":true}', + ], + ], + 'nullable null' => [ + [ + 'id' => 'int', + 'remark' => '?json-array', + ], + [ + 'id' => 1, + 'remark' => null, + ], + [ + 'id' => 1, + 'remark' => null, + ], + ], + 'nullable not null' => [ + [ + 'id' => 'int', + 'remark' => '?json-array', + ], + [ + 'id' => 1, + 'remark' => ['foo' => 'bar', 'baz' => true], + ], + [ + 'id' => 1, + 'remark' => '{"foo":"bar","baz":true}', + ], + ], + 'int-bool' => [ + [ + 'id' => 'int', + 'active' => 'int-bool', + ], + [ + 'id' => 1, + 'active' => true, + ], + [ + 'id' => 1, + 'active' => 1, + ], + ], + 'array' => [ + [ + 'id' => 'int', + 'attr' => 'array', + ], + [ + 'id' => 1, + 'attr' => ['foo' => 'bar'], + ], + [ + 'id' => 1, + 'attr' => 'a:1:{s:3:"foo";s:3:"bar";}', + ], + ], + 'bool 1' => [ + [ + 'id' => 'int', + 'active' => 'bool', + ], + [ + 'id' => 1, + 'active' => true, + ], + [ + 'id' => 1, + 'active' => true, + ], + ], + 'csv' => [ + [ + 'id' => 'int', + 'data' => 'csv', + ], + [ + 'id' => 1, + 'data' => ['foo', 'bar', 'bam'], + ], + [ + 'id' => 1, + 'data' => 'foo,bar,bam', + ], + ], + 'float' => [ + [ + 'id' => 'int', + 'temp' => 'float', + ], + [ + 'id' => 1, + 'temp' => 15.9, + ], + [ + 'id' => 1, + 'temp' => 15.9, + ], + ], + ]; + } + + public function testDateTimeConvertDataFromDB(): void + { + $types = [ + 'id' => 'int', + 'date' => 'datetime', + ]; + $converter = new DataConverter($types); + + $dbData = [ + 'id' => '1', + 'date' => '2023-11-18 14:18:18', + ]; + $data = $converter->fromDatabase($dbData); + + $this->assertInstanceOf(Time::class, $data['date']); + $expectedDate = Time::parse('2023-11-18 14:18:18'); + $this->assertSame($expectedDate->getTimestamp(), $data['date']->getTimestamp()); + } + + public function testDateTimeConvertDataToDB(): void + { + $types = [ + 'id' => 'int', + 'date' => 'datetime', + ]; + $converter = new DataConverter($types); + + $phpData = [ + 'id' => '1', + 'date' => Time::parse('2023-11-18 14:18:18'), + ]; + $data = $converter->toDatabase($phpData); + + $this->assertSame('2023-11-18 14:18:18', $data['date']); + } + + public function testTimestampConvertDataFromDB(): void + { + $types = [ + 'id' => 'int', + 'date' => 'timestamp', + ]; + $converter = new DataConverter($types); + + $dbData = [ + 'id' => '1', + 'date' => '1700285831', + ]; + $data = $converter->fromDatabase($dbData); + + $this->assertInstanceOf(Time::class, $data['date']); + $this->assertSame(1_700_285_831, $data['date']->getTimestamp()); + } + + public function testTimestampConvertDataToDB(): void + { + $types = [ + 'id' => 'int', + 'date' => 'timestamp', + ]; + $converter = new DataConverter($types); + + $phpData = [ + 'id' => '1', + 'date' => Time::createFromTimestamp(1_700_285_831), + ]; + $data = $converter->toDatabase($phpData); + + $this->assertSame(1_700_285_831, $data['date']); + } + + public function testURIConvertDataFromDB(): void + { + $types = [ + 'id' => 'int', + 'url' => 'uri', + ]; + $converter = new DataConverter($types); + + $dbData = [ + 'id' => '1', + 'url' => 'http://example.com/', + ]; + $data = $converter->fromDatabase($dbData); + + $this->assertInstanceOf(URI::class, $data['url']); + $this->assertSame('http://example.com/', (string) $data['url']); + } + + public function testURIConvertDataToDB(): void + { + $types = [ + 'id' => 'int', + 'url' => 'uri', + ]; + $converter = new DataConverter($types); + + $phpData = [ + 'id' => '1', + 'url' => new URI('http://example.com/'), + ]; + $data = $converter->toDatabase($phpData); + + $this->assertSame('http://example.com/', $data['url']); + } + + public function testInvalidType(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No such handler. Invalid type: invalid'); + + $types = [ + 'id' => 'invalid', + 'remark' => 'json-array', + ]; + $converter = new DataConverter($types); + + $dbData = [ + 'id' => '1', + 'remark' => '{"foo":"bar", "baz":true}', + ]; + $converter->fromDatabase($dbData); + } + + public function testInvalidCastHandler(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Invalid class type. It must implement CastInterface. class: CodeIgniter\Database\DataConverter\DataConverter' + ); + + $types = [ + 'id' => 'int', + 'remark' => 'json-array', + ]; + $converter = new DataConverter($types, ['int' => DataConverter::class]); + + $dbData = [ + 'id' => '1', + 'remark' => '{"foo":"bar", "baz":true}', + ]; + $converter->fromDatabase($dbData); + } +} From 69dd4cca0e5187720b9f45d2040532682d01bc6c Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 19 Nov 2023 07:47:25 +0900 Subject: [PATCH 02/38] chore: adds skip_violations --- deptrac.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deptrac.yaml b/deptrac.yaml index 4d6cb8912820..49e81587539f 100644 --- a/deptrac.yaml +++ b/deptrac.yaml @@ -228,6 +228,8 @@ parameters: - CodeIgniter\HTTP\Header - CodeIgniter\HTTP\IncomingRequest - CodeIgniter\HTTP\ResponseInterface + CodeIgniter\Database\DataConverter\Cast\URICast: + - CodeIgniter\HTTP\URI CodeIgniter\Entity\Cast\URICast: - CodeIgniter\HTTP\URI CodeIgniter\Log\Handlers\ChromeLoggerHandler: From caf51d4a03ad20bce0639ed3fa100785fa5eb3e2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 20 Nov 2023 17:36:15 +0900 Subject: [PATCH 03/38] refactor: use get_debug_type() instead of var_export() --- system/Database/DataConverter/Cast/BaseCast.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index d35b761622f5..3d3ec35946ef 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -39,6 +39,6 @@ public static function toDatabase($value, array $params = []) */ protected static function invalidTypeValueError(mixed $value): never { - throw new TypeError('Invalid type value: ' . var_export($value, true)); + throw new TypeError('Invalid type: ' . get_debug_type($value)); } } From 8554b714cafe576236fba414b743b18d652ddc4e Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 22 Nov 2023 15:00:40 +0900 Subject: [PATCH 04/38] test: extract createDataConverter() --- .../DataConverter/DataConverterTest.php | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/system/Database/DataConverter/DataConverterTest.php b/tests/system/Database/DataConverter/DataConverterTest.php index 9815aeccc4f5..518e69983e02 100644 --- a/tests/system/Database/DataConverter/DataConverterTest.php +++ b/tests/system/Database/DataConverter/DataConverterTest.php @@ -29,7 +29,7 @@ public function testInstantiate(): void 'id' => 'int', 'remark' => 'json', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $this->assertInstanceOf(DataConverter::class, $converter); } @@ -39,7 +39,7 @@ public function testInstantiate(): void */ public function testConvertDataFromDB(array $types, array $dbData, array $expected): void { - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $data = $converter->fromDatabase($dbData); @@ -51,7 +51,7 @@ public function testConvertDataFromDB(array $types, array $dbData, array $expect */ public function testConvertDataToDB(array $types, array $phpData, array $expected): void { - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $data = $converter->toDatabase($phpData); @@ -328,7 +328,7 @@ public function testDateTimeConvertDataFromDB(): void 'id' => 'int', 'date' => 'datetime', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $dbData = [ 'id' => '1', @@ -347,7 +347,7 @@ public function testDateTimeConvertDataToDB(): void 'id' => 'int', 'date' => 'datetime', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $phpData = [ 'id' => '1', @@ -364,7 +364,7 @@ public function testTimestampConvertDataFromDB(): void 'id' => 'int', 'date' => 'timestamp', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $dbData = [ 'id' => '1', @@ -382,7 +382,7 @@ public function testTimestampConvertDataToDB(): void 'id' => 'int', 'date' => 'timestamp', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $phpData = [ 'id' => '1', @@ -399,7 +399,7 @@ public function testURIConvertDataFromDB(): void 'id' => 'int', 'url' => 'uri', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $dbData = [ 'id' => '1', @@ -417,7 +417,7 @@ public function testURIConvertDataToDB(): void 'id' => 'int', 'url' => 'uri', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $phpData = [ 'id' => '1', @@ -437,7 +437,7 @@ public function testInvalidType(): void 'id' => 'invalid', 'remark' => 'json-array', ]; - $converter = new DataConverter($types); + $converter = $this->createDataConverter($types); $dbData = [ 'id' => '1', @@ -457,7 +457,7 @@ public function testInvalidCastHandler(): void 'id' => 'int', 'remark' => 'json-array', ]; - $converter = new DataConverter($types, ['int' => DataConverter::class]); + $converter = $this->createDataConverter($types, ['int' => DataConverter::class]); $dbData = [ 'id' => '1', @@ -465,4 +465,9 @@ public function testInvalidCastHandler(): void ]; $converter->fromDatabase($dbData); } + + private function createDataConverter(array $types, array $handlers = []): DataConverter + { + return new DataConverter($types, $handlers); + } } From c15bbbe5218301d9f331a66b31562b41c66f2e3b Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 22 Nov 2023 15:49:52 +0900 Subject: [PATCH 05/38] refactor: fix PHPStan types --- .../Database/DataConverter/Cast/ArrayCast.php | 14 +++++-------- .../Database/DataConverter/Cast/BaseCast.php | 18 ++++++++--------- .../DataConverter/Cast/BooleanCast.php | 6 ++++-- .../Database/DataConverter/Cast/CSVCast.php | 8 +++++--- .../DataConverter/Cast/CastInterface.php | 20 ++++++++++--------- .../DataConverter/Cast/DatetimeCast.php | 8 +++++--- .../Database/DataConverter/Cast/FloatCast.php | 6 ++++-- .../DataConverter/Cast/IntBoolCast.php | 8 +++++--- .../DataConverter/Cast/IntegerCast.php | 6 ++++-- .../Database/DataConverter/Cast/JsonCast.php | 10 +++++----- .../DataConverter/Cast/TimestampCast.php | 8 +++++--- .../Database/DataConverter/Cast/URICast.php | 8 +++++--- 12 files changed, 66 insertions(+), 54 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index 4b41d9367d49..a9e7d58c2523 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -14,14 +14,13 @@ /** * Class ArrayCast * - * DB column: string <--> PHP: array + * PHP: array <--> DB column: string + * + * @extends BaseCast */ class ArrayCast extends BaseCast implements CastInterface { - /** - * {@inheritDoc} - */ - public static function fromDatabase($value, array $params = []): array + public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -34,10 +33,7 @@ public static function fromDatabase($value, array $params = []): array return (array) $value; } - /** - * {@inheritDoc} - */ - public static function toDatabase($value, array $params = []): string + public static function toDatabase(mixed $value, array $params = []): string { return serialize($value); } diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index 3d3ec35946ef..e4661608c8d2 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -14,28 +14,26 @@ use TypeError; /** - * Class BaseCast + * @template TPhpValue PHP data type + * @template TToDb Data type to pass to database driver + * @template TDbColumn Data type from database driver + * + * @implements CastInterface */ abstract class BaseCast implements CastInterface { - /** - * {@inheritDoc} - */ - public static function fromDatabase($value, array $params = []) + public static function fromDatabase(mixed $value, array $params = []): mixed { return $value; } - /** - * {@inheritDoc} - */ - public static function toDatabase($value, array $params = []) + public static function toDatabase(mixed $value, array $params = []): mixed { return $value; } /** - * Throws TypeError + * @throws TypeError */ protected static function invalidTypeValueError(mixed $value): never { diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/Database/DataConverter/Cast/BooleanCast.php index 2f7a50eca800..f88e0dc8f3f9 100644 --- a/system/Database/DataConverter/Cast/BooleanCast.php +++ b/system/Database/DataConverter/Cast/BooleanCast.php @@ -14,14 +14,16 @@ /** * Class BooleanCast * - * DB column: bool|int(0/1) <--> PHP: bool + * PHP: bool <--> DB column: bool|int(0/1) + * + * @extends BaseCast */ class BooleanCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): bool + public static function fromDatabase(mixed $value, array $params = []): bool { // For PostgreSQL if ($value === 't') { diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index b1af9ea0597f..3910e865c764 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -14,14 +14,16 @@ /** * Class CSVCast * - * DB column: string <--> PHP: array + * PHP: array <--> DB column: string + * + * @extends BaseCast */ class CSVCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): array + public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -33,7 +35,7 @@ public static function fromDatabase($value, array $params = []): array /** * {@inheritDoc} */ - public static function toDatabase($value, array $params = []): string + public static function toDatabase(mixed $value, array $params = []): string { if (! is_array($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/CastInterface.php b/system/Database/DataConverter/Cast/CastInterface.php index 540623ea3339..a6d8b4550552 100644 --- a/system/Database/DataConverter/Cast/CastInterface.php +++ b/system/Database/DataConverter/Cast/CastInterface.php @@ -12,27 +12,29 @@ namespace CodeIgniter\Database\DataConverter\Cast; /** - * Interface CastInterface + * @template TPhpValue PHP data type + * @template TToDb Data type to pass to database driver + * @template TDbColumn Data type from database driver */ interface CastInterface { /** * Takes value from database, returns its value for PHP. * - * @param bool|float|int|string|null $value Data - * @param array $params Additional param + * @param TDbColumn $value Data from database driver + * @param list $params Additional param * - * @return array|bool|float|int|object|string|null + * @return TPhpValue */ - public static function fromDatabase($value, array $params = []); + public static function fromDatabase(mixed $value, array $params = []): mixed; /** * Takes the PHP value, returns its value for database. * - * @param array|bool|float|int|object|string|null $value Data - * @param array $params Additional param + * @param TPhpValue $value PHP data + * @param list $params Additional param * - * @return bool|float|int|string|null + * @return TToDb Data to pass to database driver */ - public static function toDatabase($value, array $params = []); + public static function toDatabase(mixed $value, array $params = []): mixed; } diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index d72c96076420..d1b0346dffef 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -17,7 +17,9 @@ /** * Class DatetimeCast * - * DB column: datetime <--> PHP: Time + * PHP: Time <--> DB column: datetime + * + * @extends BaseCast */ class DatetimeCast extends BaseCast { @@ -26,7 +28,7 @@ class DatetimeCast extends BaseCast * * @throws Exception */ - public static function fromDatabase($value, array $params = []): Time + public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -38,7 +40,7 @@ public static function fromDatabase($value, array $params = []): Time /** * {@inheritDoc} */ - public static function toDatabase($value, array $params = []): string + public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof Time) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/Database/DataConverter/Cast/FloatCast.php index 974b747aa170..072d3cc221f5 100644 --- a/system/Database/DataConverter/Cast/FloatCast.php +++ b/system/Database/DataConverter/Cast/FloatCast.php @@ -14,14 +14,16 @@ /** * Class FloatCast * - * DB column: float <--> PHP: float + * PHP: float <--> DB column: float + * + * @extends BaseCast */ class FloatCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): float + public static function fromDatabase(mixed $value, array $params = []): float { return (float) $value; } diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 4a88e829d4aa..692af977d077 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -14,14 +14,16 @@ /** * Int Bool Cast * - * DB column: int(0/1) <--> PHP: bool + * PHP: bool DB <--> column: int(0/1) + * + * @extends BaseCast */ final class IntBoolCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): bool + public static function fromDatabase(mixed $value, array $params = []): bool { if (! is_int($value) && ! is_string($value)) { self::invalidTypeValueError($value); @@ -33,7 +35,7 @@ public static function fromDatabase($value, array $params = []): bool /** * {@inheritDoc} */ - public static function toDatabase($value, array $params = []): int + public static function toDatabase(mixed $value, array $params = []): int { if (! is_bool($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index 14c9862e8152..5d1eab94bda9 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -14,14 +14,16 @@ /** * Class IntegerCast * - * DB column: int <--> PHP: int + * PHP: int <--> DB column: int + * + * @extends BaseCast */ class IntegerCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): int + public static function fromDatabase(mixed $value, array $params = []): int { if (! is_string($value) && ! is_int($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 83c7f66ee7e3..38c977e5b887 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -18,14 +18,16 @@ /** * Class JsonCast * - * DB column: string <--> PHP: array|stdClass + * PHP: array|stdClass <--> DB column: string + * + * @extends BaseCast */ class JsonCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []) + public static function fromDatabase(mixed $value, array $params = []): array|stdClass { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -51,10 +53,8 @@ public static function fromDatabase($value, array $params = []) /** * {@inheritDoc} - * - * @param mixed $value */ - public static function toDatabase($value, array $params = []): string + public static function toDatabase(mixed $value, array $params = []): string { try { $output = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index e20414bf2cc1..5e93fd772b5a 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -16,14 +16,16 @@ /** * Class TimestampCast * - * DB column: timestamp <--> PHP: Time + * PHP: Time <--> DB column: timestamp + * + * @extends BaseCast */ class TimestampCast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): Time + public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_int($value) && ! is_string($value)) { self::invalidTypeValueError($value); @@ -35,7 +37,7 @@ public static function fromDatabase($value, array $params = []): Time /** * {@inheritDoc} */ - public static function toDatabase($value, array $params = []): int + public static function toDatabase(mixed $value, array $params = []): int { if (! $value instanceof Time) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index 481a089f62cb..067608b3b77b 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -16,14 +16,16 @@ /** * Class URICast * - * DB column: string <--> PHP: URI + * PHP: URI <--> DB column: string + * + * @extends BaseCast */ class URICast extends BaseCast { /** * {@inheritDoc} */ - public static function fromDatabase($value, array $params = []): URI + public static function fromDatabase(mixed $value, array $params = []): URI { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -35,7 +37,7 @@ public static function fromDatabase($value, array $params = []): URI /** * {@inheritDoc} */ - public static function toDatabase($value, array $params = []): string + public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof URI) { self::invalidTypeValueError($value); From caa43a0f95727c6b9fa8fccbfd995aa89f077173 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 22 Nov 2023 16:04:55 +0900 Subject: [PATCH 06/38] chore: vendor/bin/psalm --set-baseline=psalm-baseline.xml --- psalm-baseline.xml | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c4240e2b7fc8..6b648e972008 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -26,12 +26,6 @@ Memcache|Memcached - - - $routeWithoutController - $routeWithoutController - - |parser_callable_string|parser_callable>]]> @@ -91,6 +85,17 @@ db->transStatus]]> + + + CastInterface + + + + + TDbColumn + TPhpValue + + OCI_COMMIT_ON_SUCCESS @@ -132,11 +137,6 @@ #[ReturnTypeWillChange] - - - dom = &$this->domParser]]> - - |parser_callable_string|parser_callable>]]> @@ -164,11 +164,6 @@ $this - - - $command - - UnexsistenceClass @@ -181,12 +176,6 @@ - - - username]]> - username]]> - - OCI_ASSOC From cf3bb3fd720bed2fc9f5920e6ca73672cf8063d6 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 22 Nov 2023 16:31:26 +0900 Subject: [PATCH 07/38] docs: fix doc comment DB column type --- system/Database/DataConverter/Cast/TimestampCast.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 5e93fd772b5a..3699b12b32c4 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -16,7 +16,7 @@ /** * Class TimestampCast * - * PHP: Time <--> DB column: timestamp + * PHP: Time <--> DB column: int * * @extends BaseCast */ From e04066701dec05fc51649cec781d00409676b3af Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 13:18:16 +0900 Subject: [PATCH 08/38] docs: fix PHPDoc types --- system/Database/DataConverter/DataConverter.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/Database/DataConverter/DataConverter.php b/system/Database/DataConverter/DataConverter.php index 36b1bc20f7a5..0e9a37357b92 100644 --- a/system/Database/DataConverter/DataConverter.php +++ b/system/Database/DataConverter/DataConverter.php @@ -107,12 +107,12 @@ public function toDatabase(array $phpData): array * Add ? at the beginning of $type (i.e. ?string) to get `null` * instead of casting $value if ($value === null). * - * @param bool|float|int|string|null $value The value to convert - * @param string $column The column name - * @param string $method Allowed to "fromDatabase" and "toDatabase" + * @param mixed $value The value to convert + * @param string $column The column name + * @param string $method Allowed to "fromDatabase" and "toDatabase" * @phpstan-param 'fromDatabase'|'toDatabase' $method * - * @return array|bool|float|int|object|string|null + * @return mixed */ protected function castAs($value, string $column, string $method = 'fromDatabase') { From 479d3fb639826e340c85edf4bd3c717d4e45a1cf Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 14:30:23 +0900 Subject: [PATCH 09/38] feat: improve error messages --- .../Database/DataConverter/Cast/ArrayCast.php | 2 +- .../Database/DataConverter/Cast/BaseCast.php | 9 ++++++-- .../Database/DataConverter/Cast/CSVCast.php | 4 ++-- .../DataConverter/Cast/DatetimeCast.php | 4 ++-- .../DataConverter/Cast/IntBoolCast.php | 4 ++-- .../DataConverter/Cast/IntegerCast.php | 2 +- .../Database/DataConverter/Cast/JsonCast.php | 2 +- .../DataConverter/Cast/TimestampCast.php | 4 ++-- .../Database/DataConverter/Cast/URICast.php | 4 ++-- .../Database/DataConverter/DataConverter.php | 2 +- .../DataConverter/DataConverterTest.php | 23 ++++++++++++++++++- 11 files changed, 43 insertions(+), 17 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index a9e7d58c2523..a97dd366c6d9 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -23,7 +23,7 @@ class ArrayCast extends BaseCast implements CastInterface public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } if ((strpos($value, 'a:') === 0 || strpos($value, 's:') === 0)) { diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index e4661608c8d2..293f1752f2a8 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -35,8 +35,13 @@ public static function toDatabase(mixed $value, array $params = []): mixed /** * @throws TypeError */ - protected static function invalidTypeValueError(mixed $value): never + protected static function invalidTypeValueError(mixed $value, string $class): never { - throw new TypeError('Invalid type: ' . get_debug_type($value)); + $message = '[' . $class . '] Invalid value type: ' . get_debug_type($value); + if (is_scalar($value)) { + $message .= ', and its value: ' . $value; + } + + throw new TypeError($message); } } diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index 3910e865c764..60efde20958a 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -26,7 +26,7 @@ class CSVCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return explode(',', $value); @@ -38,7 +38,7 @@ public static function fromDatabase(mixed $value, array $params = []): array public static function toDatabase(mixed $value, array $params = []): string { if (! is_array($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return implode(',', $value); diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index d1b0346dffef..0758a7552553 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -31,7 +31,7 @@ class DatetimeCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return Time::parse($value); @@ -43,7 +43,7 @@ public static function fromDatabase(mixed $value, array $params = []): Time public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof Time) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return (string) $value; diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 692af977d077..5ba38d1a421b 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -26,7 +26,7 @@ final class IntBoolCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): bool { if (! is_int($value) && ! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return (bool) $value; @@ -38,7 +38,7 @@ public static function fromDatabase(mixed $value, array $params = []): bool public static function toDatabase(mixed $value, array $params = []): int { if (! is_bool($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return (int) $value; diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index 5d1eab94bda9..064ed1b5f1a7 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -26,7 +26,7 @@ class IntegerCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): int { if (! is_string($value) && ! is_int($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return (int) $value; diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 38c977e5b887..6ffe71aefb9d 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -30,7 +30,7 @@ class JsonCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): array|stdClass { if (! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } $associative = in_array('array', $params, true); diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 3699b12b32c4..9d1dcffc518f 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -28,7 +28,7 @@ class TimestampCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_int($value) && ! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return Time::createFromTimestamp((int) $value); @@ -40,7 +40,7 @@ public static function fromDatabase(mixed $value, array $params = []): Time public static function toDatabase(mixed $value, array $params = []): int { if (! $value instanceof Time) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return $value->getTimestamp(); diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index 067608b3b77b..5b076518f8f1 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -28,7 +28,7 @@ class URICast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): URI { if (! is_string($value)) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return new URI($value); @@ -40,7 +40,7 @@ public static function fromDatabase(mixed $value, array $params = []): URI public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof URI) { - self::invalidTypeValueError($value); + self::invalidTypeValueError($value, self::class); } return (string) $value; diff --git a/system/Database/DataConverter/DataConverter.php b/system/Database/DataConverter/DataConverter.php index 0e9a37357b92..c4c022044541 100644 --- a/system/Database/DataConverter/DataConverter.php +++ b/system/Database/DataConverter/DataConverter.php @@ -157,7 +157,7 @@ protected function castAs($value, string $column, string $method = 'fromDatabase $handlers = array_merge($this->defaultCastHandlers, $this->castHandlers); if (! isset($handlers[$type])) { - throw new InvalidArgumentException('No such handler. Invalid type: ' . $type); + throw new InvalidArgumentException('No such handler for "' . $column . '". Invalid type: ' . $type); } $handler = $handlers[$type]; diff --git a/tests/system/Database/DataConverter/DataConverterTest.php b/tests/system/Database/DataConverter/DataConverterTest.php index 518e69983e02..a81a3cb1b9d2 100644 --- a/tests/system/Database/DataConverter/DataConverterTest.php +++ b/tests/system/Database/DataConverter/DataConverterTest.php @@ -15,6 +15,7 @@ use CodeIgniter\I18n\Time; use CodeIgniter\Test\CIUnitTestCase; use InvalidArgumentException; +use TypeError; /** * @internal @@ -431,7 +432,7 @@ public function testURIConvertDataToDB(): void public function testInvalidType(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('No such handler. Invalid type: invalid'); + $this->expectExceptionMessage('No such handler for "id". Invalid type: invalid'); $types = [ 'id' => 'invalid', @@ -446,6 +447,26 @@ public function testInvalidType(): void $converter->fromDatabase($dbData); } + public function testInvalidValue(): void + { + $this->expectException(TypeError::class); + $this->expectExceptionMessage( + '[CodeIgniter\Database\DataConverter\Cast\JsonCast] Invalid value type: bool, and its value: 1' + ); + + $types = [ + 'id' => 'int', + 'remark' => 'json-array', + ]; + $converter = $this->createDataConverter($types); + + $dbData = [ + 'id' => '1', + 'remark' => true, + ]; + $converter->fromDatabase($dbData); + } + public function testInvalidCastHandler(): void { $this->expectException(InvalidArgumentException::class); From 83574abd34a32163d9c86690b258aaeaa29d667e Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 18:41:18 +0900 Subject: [PATCH 10/38] refactor: use var_export() --- system/Database/DataConverter/Cast/BaseCast.php | 2 +- tests/system/Database/DataConverter/DataConverterTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index 293f1752f2a8..34f93403827e 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -39,7 +39,7 @@ protected static function invalidTypeValueError(mixed $value, string $class): ne { $message = '[' . $class . '] Invalid value type: ' . get_debug_type($value); if (is_scalar($value)) { - $message .= ', and its value: ' . $value; + $message .= ', and its value: ' . var_export($value, true); } throw new TypeError($message); diff --git a/tests/system/Database/DataConverter/DataConverterTest.php b/tests/system/Database/DataConverter/DataConverterTest.php index a81a3cb1b9d2..c6e2f7ccfd5c 100644 --- a/tests/system/Database/DataConverter/DataConverterTest.php +++ b/tests/system/Database/DataConverter/DataConverterTest.php @@ -451,7 +451,7 @@ public function testInvalidValue(): void { $this->expectException(TypeError::class); $this->expectExceptionMessage( - '[CodeIgniter\Database\DataConverter\Cast\JsonCast] Invalid value type: bool, and its value: 1' + '[CodeIgniter\Database\DataConverter\Cast\JsonCast] Invalid value type: bool, and its value: true' ); $types = [ From 323a3d3e9dfa7b26acf540fdbfa76c5b15590037 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 18:41:32 +0900 Subject: [PATCH 11/38] refactor: use static::class --- system/Database/DataConverter/Cast/ArrayCast.php | 2 +- system/Database/DataConverter/Cast/BaseCast.php | 4 ++-- system/Database/DataConverter/Cast/CSVCast.php | 4 ++-- system/Database/DataConverter/Cast/DatetimeCast.php | 4 ++-- system/Database/DataConverter/Cast/IntBoolCast.php | 4 ++-- system/Database/DataConverter/Cast/IntegerCast.php | 2 +- system/Database/DataConverter/Cast/JsonCast.php | 2 +- system/Database/DataConverter/Cast/TimestampCast.php | 4 ++-- system/Database/DataConverter/Cast/URICast.php | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index a97dd366c6d9..a9e7d58c2523 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -23,7 +23,7 @@ class ArrayCast extends BaseCast implements CastInterface public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } if ((strpos($value, 'a:') === 0 || strpos($value, 's:') === 0)) { diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index 34f93403827e..321441283fc5 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -35,9 +35,9 @@ public static function toDatabase(mixed $value, array $params = []): mixed /** * @throws TypeError */ - protected static function invalidTypeValueError(mixed $value, string $class): never + protected static function invalidTypeValueError(mixed $value): never { - $message = '[' . $class . '] Invalid value type: ' . get_debug_type($value); + $message = '[' . static::class . '] Invalid value type: ' . get_debug_type($value); if (is_scalar($value)) { $message .= ', and its value: ' . var_export($value, true); } diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index 60efde20958a..3910e865c764 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -26,7 +26,7 @@ class CSVCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return explode(',', $value); @@ -38,7 +38,7 @@ public static function fromDatabase(mixed $value, array $params = []): array public static function toDatabase(mixed $value, array $params = []): string { if (! is_array($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return implode(',', $value); diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index 0758a7552553..d1b0346dffef 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -31,7 +31,7 @@ class DatetimeCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return Time::parse($value); @@ -43,7 +43,7 @@ public static function fromDatabase(mixed $value, array $params = []): Time public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof Time) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return (string) $value; diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 5ba38d1a421b..692af977d077 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -26,7 +26,7 @@ final class IntBoolCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): bool { if (! is_int($value) && ! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return (bool) $value; @@ -38,7 +38,7 @@ public static function fromDatabase(mixed $value, array $params = []): bool public static function toDatabase(mixed $value, array $params = []): int { if (! is_bool($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return (int) $value; diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index 064ed1b5f1a7..5d1eab94bda9 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -26,7 +26,7 @@ class IntegerCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): int { if (! is_string($value) && ! is_int($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return (int) $value; diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 6ffe71aefb9d..38c977e5b887 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -30,7 +30,7 @@ class JsonCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): array|stdClass { if (! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } $associative = in_array('array', $params, true); diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 9d1dcffc518f..3699b12b32c4 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -28,7 +28,7 @@ class TimestampCast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_int($value) && ! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return Time::createFromTimestamp((int) $value); @@ -40,7 +40,7 @@ public static function fromDatabase(mixed $value, array $params = []): Time public static function toDatabase(mixed $value, array $params = []): int { if (! $value instanceof Time) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return $value->getTimestamp(); diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index 5b076518f8f1..067608b3b77b 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -28,7 +28,7 @@ class URICast extends BaseCast public static function fromDatabase(mixed $value, array $params = []): URI { if (! is_string($value)) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return new URI($value); @@ -40,7 +40,7 @@ public static function fromDatabase(mixed $value, array $params = []): URI public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof URI) { - self::invalidTypeValueError($value, self::class); + self::invalidTypeValueError($value); } return (string) $value; From 3447d0102db0c10bb0f448a4ac67e05c9053b820 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 20:26:59 +0900 Subject: [PATCH 12/38] docs: add {@inheritDoc} --- system/Database/DataConverter/Cast/ArrayCast.php | 6 ++++++ system/Database/DataConverter/Cast/BaseCast.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index a9e7d58c2523..d23b571c8d16 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -20,6 +20,9 @@ */ class ArrayCast extends BaseCast implements CastInterface { + /** + * {@inheritDoc} + */ public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { @@ -33,6 +36,9 @@ public static function fromDatabase(mixed $value, array $params = []): array return (array) $value; } + /** + * {@inheritDoc} + */ public static function toDatabase(mixed $value, array $params = []): string { return serialize($value); diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index 321441283fc5..7075d9751e4c 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -22,11 +22,17 @@ */ abstract class BaseCast implements CastInterface { + /** + * {@inheritDoc} + */ public static function fromDatabase(mixed $value, array $params = []): mixed { return $value; } + /** + * {@inheritDoc} + */ public static function toDatabase(mixed $value, array $params = []): mixed { return $value; From 943599b77ce8cd5373590c8aaf47bd83c3899f69 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 20:46:42 +0900 Subject: [PATCH 13/38] docs: fix TDbColumn type The type of the value from the database is not guaranteed. Therefore, the type should be mixed. --- system/Database/DataConverter/Cast/ArrayCast.php | 2 +- system/Database/DataConverter/Cast/BooleanCast.php | 2 +- system/Database/DataConverter/Cast/CSVCast.php | 2 +- system/Database/DataConverter/Cast/DatetimeCast.php | 5 +---- system/Database/DataConverter/Cast/FloatCast.php | 6 +++++- system/Database/DataConverter/Cast/IntBoolCast.php | 2 +- system/Database/DataConverter/Cast/IntegerCast.php | 2 +- system/Database/DataConverter/Cast/JsonCast.php | 2 +- system/Database/DataConverter/Cast/TimestampCast.php | 2 +- system/Database/DataConverter/Cast/URICast.php | 2 +- 10 files changed, 14 insertions(+), 13 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index d23b571c8d16..a0aa5489aebe 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -16,7 +16,7 @@ * * PHP: array <--> DB column: string * - * @extends BaseCast + * @extends BaseCast */ class ArrayCast extends BaseCast implements CastInterface { diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/Database/DataConverter/Cast/BooleanCast.php index f88e0dc8f3f9..b00d13f22ef2 100644 --- a/system/Database/DataConverter/Cast/BooleanCast.php +++ b/system/Database/DataConverter/Cast/BooleanCast.php @@ -16,7 +16,7 @@ * * PHP: bool <--> DB column: bool|int(0/1) * - * @extends BaseCast + * @extends BaseCast */ class BooleanCast extends BaseCast { diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index 3910e865c764..29105089cd7f 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -16,7 +16,7 @@ * * PHP: array <--> DB column: string * - * @extends BaseCast + * @extends BaseCast */ class CSVCast extends BaseCast { diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index d1b0346dffef..c8b2cb5dad45 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -12,21 +12,18 @@ namespace CodeIgniter\Database\DataConverter\Cast; use CodeIgniter\I18n\Time; -use Exception; /** * Class DatetimeCast * * PHP: Time <--> DB column: datetime * - * @extends BaseCast + * @extends BaseCast */ class DatetimeCast extends BaseCast { /** * {@inheritDoc} - * - * @throws Exception */ public static function fromDatabase(mixed $value, array $params = []): Time { diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/Database/DataConverter/Cast/FloatCast.php index 072d3cc221f5..9edb26b8eec0 100644 --- a/system/Database/DataConverter/Cast/FloatCast.php +++ b/system/Database/DataConverter/Cast/FloatCast.php @@ -16,7 +16,7 @@ * * PHP: float <--> DB column: float * - * @extends BaseCast + * @extends BaseCast */ class FloatCast extends BaseCast { @@ -25,6 +25,10 @@ class FloatCast extends BaseCast */ public static function fromDatabase(mixed $value, array $params = []): float { + if (! is_float($value) && ! is_string($value)) { + self::invalidTypeValueError($value); + } + return (float) $value; } } diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 692af977d077..578c4055a525 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -16,7 +16,7 @@ * * PHP: bool DB <--> column: int(0/1) * - * @extends BaseCast + * @extends BaseCast */ final class IntBoolCast extends BaseCast { diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index 5d1eab94bda9..b455721303aa 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -16,7 +16,7 @@ * * PHP: int <--> DB column: int * - * @extends BaseCast + * @extends BaseCast */ class IntegerCast extends BaseCast { diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 38c977e5b887..6c688e5ad599 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -20,7 +20,7 @@ * * PHP: array|stdClass <--> DB column: string * - * @extends BaseCast + * @extends BaseCast */ class JsonCast extends BaseCast { diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 3699b12b32c4..2ddf6b3f48f4 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -18,7 +18,7 @@ * * PHP: Time <--> DB column: int * - * @extends BaseCast + * @extends BaseCast */ class TimestampCast extends BaseCast { diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index 067608b3b77b..889ad7f4b373 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -18,7 +18,7 @@ * * PHP: URI <--> DB column: string * - * @extends BaseCast + * @extends BaseCast */ class URICast extends BaseCast { From 8630f5d0dd464af06aa9a96b222a644657f71972 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 23 Nov 2023 20:49:32 +0900 Subject: [PATCH 14/38] fix: remove uneeded if conditions --- system/Database/DataConverter/Cast/JsonCast.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 6c688e5ad599..62cfd3181915 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -37,15 +37,10 @@ public static function fromDatabase(mixed $value, array $params = []): array|std $output = ($associative ? [] : new stdClass()); - if ( - (strlen($value) > 1 && in_array($value[0], ['[', '{', '"'], true)) - || is_numeric($value) - ) { - try { - $output = json_decode($value, $associative, 512, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { - throw CastException::forInvalidJsonFormat($e->getCode()); - } + try { + $output = json_decode($value, $associative, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw CastException::forInvalidJsonFormat($e->getCode()); } return $output; From 75b16ae19240221f979774be9291dd777602999f Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 07:32:19 +0900 Subject: [PATCH 15/38] docs: improve doc comments for conversions --- system/Database/DataConverter/Cast/ArrayCast.php | 3 ++- system/Database/DataConverter/Cast/BooleanCast.php | 3 ++- system/Database/DataConverter/Cast/CSVCast.php | 3 ++- system/Database/DataConverter/Cast/DatetimeCast.php | 3 ++- system/Database/DataConverter/Cast/FloatCast.php | 3 ++- system/Database/DataConverter/Cast/IntBoolCast.php | 3 ++- system/Database/DataConverter/Cast/IntegerCast.php | 3 ++- system/Database/DataConverter/Cast/JsonCast.php | 3 ++- system/Database/DataConverter/Cast/TimestampCast.php | 3 ++- system/Database/DataConverter/Cast/URICast.php | 3 ++- 10 files changed, 20 insertions(+), 10 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index a0aa5489aebe..eed0a6ab5fd2 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -14,7 +14,8 @@ /** * Class ArrayCast * - * PHP: array <--> DB column: string + * (PHP) [array --> string] --> (DB driver) --> (DB column) string + * [ <-- string] <-- (DB driver) <-- (DB column) string * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/Database/DataConverter/Cast/BooleanCast.php index b00d13f22ef2..7da4a4c5b315 100644 --- a/system/Database/DataConverter/Cast/BooleanCast.php +++ b/system/Database/DataConverter/Cast/BooleanCast.php @@ -14,7 +14,8 @@ /** * Class BooleanCast * - * PHP: bool <--> DB column: bool|int(0/1) + * (PHP) [bool --> bool ] --> (DB driver) --> (DB column) bool|int(0/1) + * [ <-- string|int] <-- (DB driver) <-- (DB column) bool|int(0/1) * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index 29105089cd7f..db80c3f08f92 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -14,7 +14,8 @@ /** * Class CSVCast * - * PHP: array <--> DB column: string + * (PHP) [array --> string] --> (DB driver) --> (DB column) string + * [ <-- string] <-- (DB driver) <-- (DB column) string * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index c8b2cb5dad45..986d3f9a373a 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -16,7 +16,8 @@ /** * Class DatetimeCast * - * PHP: Time <--> DB column: datetime + * (PHP) [Time --> string] --> (DB driver) --> (DB column) datetime + * [ <-- string] <-- (DB driver) <-- (DB column) datetime * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/Database/DataConverter/Cast/FloatCast.php index 9edb26b8eec0..17d54b4371c2 100644 --- a/system/Database/DataConverter/Cast/FloatCast.php +++ b/system/Database/DataConverter/Cast/FloatCast.php @@ -14,7 +14,8 @@ /** * Class FloatCast * - * PHP: float <--> DB column: float + * (PHP) [float --> float ] --> (DB driver) --> (DB column) float + * [ <-- float|string] <-- (DB driver) <-- (DB column) float * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 578c4055a525..5583ce121ae6 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -14,7 +14,8 @@ /** * Int Bool Cast * - * PHP: bool DB <--> column: int(0/1) + * (PHP) [bool --> int ] --> (DB driver) --> (DB column) int(0/1) + * [ <-- int|string] <-- (DB driver) <-- (DB column) int(0/1) * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index b455721303aa..bf5e4be20bf1 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -14,7 +14,8 @@ /** * Class IntegerCast * - * PHP: int <--> DB column: int + * (PHP) [int --> int ] --> (DB driver) --> (DB column) int + * [ <-- int|string] <-- (DB driver) <-- (DB column) int * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 62cfd3181915..642bcb746279 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -18,7 +18,8 @@ /** * Class JsonCast * - * PHP: array|stdClass <--> DB column: string + * (PHP) [array|stdClass --> string] --> (DB driver) --> (DB column) string + * [ <-- string] <-- (DB driver) <-- (DB column) string * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 2ddf6b3f48f4..367046ef5520 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -16,7 +16,8 @@ /** * Class TimestampCast * - * PHP: Time <--> DB column: int + * (PHP) [Time --> int ] --> (DB driver) --> (DB column) int + * [ <-- int|string] <-- (DB driver) <-- (DB column) int * * @extends BaseCast */ diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index 889ad7f4b373..e92b7de273d1 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -16,7 +16,8 @@ /** * Class URICast * - * PHP: URI <--> DB column: string + * (PHP) [URI --> string] --> (DB driver) --> (DB column) string + * [ <-- string] <-- (DB driver) <-- (DB column) string * * @extends BaseCast */ From c2ddfe9981aa2b2d88a94273f4ef3191a4184848 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 08:40:02 +0900 Subject: [PATCH 16/38] docs: remove {@inheritDoc} --- system/Database/DataConverter/Cast/ArrayCast.php | 6 ------ system/Database/DataConverter/Cast/BaseCast.php | 9 --------- system/Database/DataConverter/Cast/BooleanCast.php | 3 --- system/Database/DataConverter/Cast/CSVCast.php | 6 ------ system/Database/DataConverter/Cast/DatetimeCast.php | 6 ------ system/Database/DataConverter/Cast/FloatCast.php | 3 --- system/Database/DataConverter/Cast/IntBoolCast.php | 6 ------ system/Database/DataConverter/Cast/IntegerCast.php | 3 --- system/Database/DataConverter/Cast/JsonCast.php | 6 ------ system/Database/DataConverter/Cast/TimestampCast.php | 6 ------ system/Database/DataConverter/Cast/URICast.php | 6 ------ 11 files changed, 60 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index eed0a6ab5fd2..49c3feb76df9 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -21,9 +21,6 @@ */ class ArrayCast extends BaseCast implements CastInterface { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { @@ -37,9 +34,6 @@ public static function fromDatabase(mixed $value, array $params = []): array return (array) $value; } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): string { return serialize($value); diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index 7075d9751e4c..aa11d802b8a8 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -22,25 +22,16 @@ */ abstract class BaseCast implements CastInterface { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): mixed { return $value; } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): mixed { return $value; } - /** - * @throws TypeError - */ protected static function invalidTypeValueError(mixed $value): never { $message = '[' . static::class . '] Invalid value type: ' . get_debug_type($value); diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/Database/DataConverter/Cast/BooleanCast.php index 7da4a4c5b315..10da1fbc3370 100644 --- a/system/Database/DataConverter/Cast/BooleanCast.php +++ b/system/Database/DataConverter/Cast/BooleanCast.php @@ -21,9 +21,6 @@ */ class BooleanCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): bool { // For PostgreSQL diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index db80c3f08f92..7842b539ef47 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -21,9 +21,6 @@ */ class CSVCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): array { if (! is_string($value)) { @@ -33,9 +30,6 @@ public static function fromDatabase(mixed $value, array $params = []): array return explode(',', $value); } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): string { if (! is_array($value)) { diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index 986d3f9a373a..ec045bcc491c 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -23,9 +23,6 @@ */ class DatetimeCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_string($value)) { @@ -35,9 +32,6 @@ public static function fromDatabase(mixed $value, array $params = []): Time return Time::parse($value); } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof Time) { diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/Database/DataConverter/Cast/FloatCast.php index 17d54b4371c2..6e807ea4edf9 100644 --- a/system/Database/DataConverter/Cast/FloatCast.php +++ b/system/Database/DataConverter/Cast/FloatCast.php @@ -21,9 +21,6 @@ */ class FloatCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): float { if (! is_float($value) && ! is_string($value)) { diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 5583ce121ae6..5feb1d9fabe0 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -21,9 +21,6 @@ */ final class IntBoolCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): bool { if (! is_int($value) && ! is_string($value)) { @@ -33,9 +30,6 @@ public static function fromDatabase(mixed $value, array $params = []): bool return (bool) $value; } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): int { if (! is_bool($value)) { diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index bf5e4be20bf1..0e391338fdf0 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -21,9 +21,6 @@ */ class IntegerCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): int { if (! is_string($value) && ! is_int($value)) { diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 642bcb746279..501d8ec63611 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -25,9 +25,6 @@ */ class JsonCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): array|stdClass { if (! is_string($value)) { @@ -47,9 +44,6 @@ public static function fromDatabase(mixed $value, array $params = []): array|std return $output; } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): string { try { diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 367046ef5520..0375820a56a1 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -23,9 +23,6 @@ */ class TimestampCast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): Time { if (! is_int($value) && ! is_string($value)) { @@ -35,9 +32,6 @@ public static function fromDatabase(mixed $value, array $params = []): Time return Time::createFromTimestamp((int) $value); } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): int { if (! $value instanceof Time) { diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index e92b7de273d1..64ada4b3ebaa 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -23,9 +23,6 @@ */ class URICast extends BaseCast { - /** - * {@inheritDoc} - */ public static function fromDatabase(mixed $value, array $params = []): URI { if (! is_string($value)) { @@ -35,9 +32,6 @@ public static function fromDatabase(mixed $value, array $params = []): URI return new URI($value); } - /** - * {@inheritDoc} - */ public static function toDatabase(mixed $value, array $params = []): string { if (! $value instanceof URI) { From 905a1a5c27bbe25e95e7a591520e7a4ef1c31acb Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 08:55:37 +0900 Subject: [PATCH 17/38] refactor: rename method names --- .../Database/DataConverter/Cast/ArrayCast.php | 4 ++-- .../Database/DataConverter/Cast/BaseCast.php | 4 ++-- .../DataConverter/Cast/BooleanCast.php | 2 +- .../Database/DataConverter/Cast/CSVCast.php | 4 ++-- .../DataConverter/Cast/CastInterface.php | 8 +++---- .../DataConverter/Cast/DatetimeCast.php | 4 ++-- .../Database/DataConverter/Cast/FloatCast.php | 2 +- .../DataConverter/Cast/IntBoolCast.php | 4 ++-- .../DataConverter/Cast/IntegerCast.php | 2 +- .../Database/DataConverter/Cast/JsonCast.php | 4 ++-- .../DataConverter/Cast/TimestampCast.php | 4 ++-- .../Database/DataConverter/Cast/URICast.php | 4 ++-- .../Database/DataConverter/DataConverter.php | 22 +++++++++---------- .../DataConverter/DataConverterTest.php | 22 +++++++++---------- 14 files changed, 44 insertions(+), 46 deletions(-) diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php index 49c3feb76df9..9375e00bef84 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/Database/DataConverter/Cast/ArrayCast.php @@ -21,7 +21,7 @@ */ class ArrayCast extends BaseCast implements CastInterface { - public static function fromDatabase(mixed $value, array $params = []): array + public static function fromDataSource(mixed $value, array $params = []): array { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -34,7 +34,7 @@ public static function fromDatabase(mixed $value, array $params = []): array return (array) $value; } - public static function toDatabase(mixed $value, array $params = []): string + public static function toDataSource(mixed $value, array $params = []): string { return serialize($value); } diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php index aa11d802b8a8..121b27cfc8af 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/Database/DataConverter/Cast/BaseCast.php @@ -22,12 +22,12 @@ */ abstract class BaseCast implements CastInterface { - public static function fromDatabase(mixed $value, array $params = []): mixed + public static function fromDataSource(mixed $value, array $params = []): mixed { return $value; } - public static function toDatabase(mixed $value, array $params = []): mixed + public static function toDataSource(mixed $value, array $params = []): mixed { return $value; } diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/Database/DataConverter/Cast/BooleanCast.php index 10da1fbc3370..3943cab8ae52 100644 --- a/system/Database/DataConverter/Cast/BooleanCast.php +++ b/system/Database/DataConverter/Cast/BooleanCast.php @@ -21,7 +21,7 @@ */ class BooleanCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): bool + public static function fromDataSource(mixed $value, array $params = []): bool { // For PostgreSQL if ($value === 't') { diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/Database/DataConverter/Cast/CSVCast.php index 7842b539ef47..3eb5538a0ea9 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/Database/DataConverter/Cast/CSVCast.php @@ -21,7 +21,7 @@ */ class CSVCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): array + public static function fromDataSource(mixed $value, array $params = []): array { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -30,7 +30,7 @@ public static function fromDatabase(mixed $value, array $params = []): array return explode(',', $value); } - public static function toDatabase(mixed $value, array $params = []): string + public static function toDataSource(mixed $value, array $params = []): string { if (! is_array($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/CastInterface.php b/system/Database/DataConverter/Cast/CastInterface.php index a6d8b4550552..fb79752b5ba7 100644 --- a/system/Database/DataConverter/Cast/CastInterface.php +++ b/system/Database/DataConverter/Cast/CastInterface.php @@ -19,22 +19,22 @@ interface CastInterface { /** - * Takes value from database, returns its value for PHP. + * Takes value from DataSource, returns its value for PHP. * * @param TDbColumn $value Data from database driver * @param list $params Additional param * * @return TPhpValue */ - public static function fromDatabase(mixed $value, array $params = []): mixed; + public static function fromDataSource(mixed $value, array $params = []): mixed; /** - * Takes the PHP value, returns its value for database. + * Takes the PHP value, returns its value for DataSource. * * @param TPhpValue $value PHP data * @param list $params Additional param * * @return TToDb Data to pass to database driver */ - public static function toDatabase(mixed $value, array $params = []): mixed; + public static function toDataSource(mixed $value, array $params = []): mixed; } diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/Database/DataConverter/Cast/DatetimeCast.php index ec045bcc491c..fea1ae687d33 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/Database/DataConverter/Cast/DatetimeCast.php @@ -23,7 +23,7 @@ */ class DatetimeCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): Time + public static function fromDataSource(mixed $value, array $params = []): Time { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -32,7 +32,7 @@ public static function fromDatabase(mixed $value, array $params = []): Time return Time::parse($value); } - public static function toDatabase(mixed $value, array $params = []): string + public static function toDataSource(mixed $value, array $params = []): string { if (! $value instanceof Time) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/Database/DataConverter/Cast/FloatCast.php index 6e807ea4edf9..d19c4b840d8e 100644 --- a/system/Database/DataConverter/Cast/FloatCast.php +++ b/system/Database/DataConverter/Cast/FloatCast.php @@ -21,7 +21,7 @@ */ class FloatCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): float + public static function fromDataSource(mixed $value, array $params = []): float { if (! is_float($value) && ! is_string($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/Database/DataConverter/Cast/IntBoolCast.php index 5feb1d9fabe0..dc3642371aab 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/Database/DataConverter/Cast/IntBoolCast.php @@ -21,7 +21,7 @@ */ final class IntBoolCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): bool + public static function fromDataSource(mixed $value, array $params = []): bool { if (! is_int($value) && ! is_string($value)) { self::invalidTypeValueError($value); @@ -30,7 +30,7 @@ public static function fromDatabase(mixed $value, array $params = []): bool return (bool) $value; } - public static function toDatabase(mixed $value, array $params = []): int + public static function toDataSource(mixed $value, array $params = []): int { if (! is_bool($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/Database/DataConverter/Cast/IntegerCast.php index 0e391338fdf0..04ef5596bc23 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/Database/DataConverter/Cast/IntegerCast.php @@ -21,7 +21,7 @@ */ class IntegerCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): int + public static function fromDataSource(mixed $value, array $params = []): int { if (! is_string($value) && ! is_int($value)) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/Database/DataConverter/Cast/JsonCast.php index 501d8ec63611..a7ded03e7c7f 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/Database/DataConverter/Cast/JsonCast.php @@ -25,7 +25,7 @@ */ class JsonCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): array|stdClass + public static function fromDataSource(mixed $value, array $params = []): array|stdClass { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -44,7 +44,7 @@ public static function fromDatabase(mixed $value, array $params = []): array|std return $output; } - public static function toDatabase(mixed $value, array $params = []): string + public static function toDataSource(mixed $value, array $params = []): string { try { $output = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/Database/DataConverter/Cast/TimestampCast.php index 0375820a56a1..26fb45df2f39 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/Database/DataConverter/Cast/TimestampCast.php @@ -23,7 +23,7 @@ */ class TimestampCast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): Time + public static function fromDataSource(mixed $value, array $params = []): Time { if (! is_int($value) && ! is_string($value)) { self::invalidTypeValueError($value); @@ -32,7 +32,7 @@ public static function fromDatabase(mixed $value, array $params = []): Time return Time::createFromTimestamp((int) $value); } - public static function toDatabase(mixed $value, array $params = []): int + public static function toDataSource(mixed $value, array $params = []): int { if (! $value instanceof Time) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/Database/DataConverter/Cast/URICast.php index 64ada4b3ebaa..b5c9f237b2d9 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/Database/DataConverter/Cast/URICast.php @@ -23,7 +23,7 @@ */ class URICast extends BaseCast { - public static function fromDatabase(mixed $value, array $params = []): URI + public static function fromDataSource(mixed $value, array $params = []): URI { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -32,7 +32,7 @@ public static function fromDatabase(mixed $value, array $params = []): URI return new URI($value); } - public static function toDatabase(mixed $value, array $params = []): string + public static function toDataSource(mixed $value, array $params = []): string { if (! $value instanceof URI) { self::invalidTypeValueError($value); diff --git a/system/Database/DataConverter/DataConverter.php b/system/Database/DataConverter/DataConverter.php index c4c022044541..409ad0ad0215 100644 --- a/system/Database/DataConverter/DataConverter.php +++ b/system/Database/DataConverter/DataConverter.php @@ -25,7 +25,7 @@ use InvalidArgumentException; /** - * PHP data <==> DB data converter + * PHP data <==> DataSource data converter * * @see \CodeIgniter\Database\DataConverter\DataConverterTest */ @@ -75,28 +75,28 @@ public function __construct( } /** - * Converts data from DB to PHP array with specified type values. + * Converts data from DataSource to PHP array with specified type values. */ - public function fromDatabase(array $dbData): array + public function fromDataSource(array $dbData): array { $output = []; foreach ($dbData as $column => $value) { - $output[$column] = $this->castAs($value, $column, 'fromDatabase'); + $output[$column] = $this->castAs($value, $column, 'fromDataSource'); } return $output; } /** - * Converts PHP array to data for DB column types. + * Converts PHP array to data for DataSource column types. */ - public function toDatabase(array $phpData): array + public function toDataSource(array $phpData): array { $output = []; foreach ($phpData as $column => $value) { - $output[$column] = $this->castAs($value, $column, 'toDatabase'); + $output[$column] = $this->castAs($value, $column, 'toDataSource'); } return $output; @@ -109,12 +109,10 @@ public function toDatabase(array $phpData): array * * @param mixed $value The value to convert * @param string $column The column name - * @param string $method Allowed to "fromDatabase" and "toDatabase" - * @phpstan-param 'fromDatabase'|'toDatabase' $method - * - * @return mixed + * @param string $method Allowed to "fromDataSource" and "toDataSource" + * @phpstan-param 'fromDataSource'|'toDataSource' $method */ - protected function castAs($value, string $column, string $method = 'fromDatabase') + protected function castAs($value, string $column, string $method = 'fromDataSource'): mixed { // If the type is not defined, return as it is. if (! isset($this->types[$column])) { diff --git a/tests/system/Database/DataConverter/DataConverterTest.php b/tests/system/Database/DataConverter/DataConverterTest.php index c6e2f7ccfd5c..7dc89d2dbe7a 100644 --- a/tests/system/Database/DataConverter/DataConverterTest.php +++ b/tests/system/Database/DataConverter/DataConverterTest.php @@ -42,7 +42,7 @@ public function testConvertDataFromDB(array $types, array $dbData, array $expect { $converter = $this->createDataConverter($types); - $data = $converter->fromDatabase($dbData); + $data = $converter->fromDataSource($dbData); $this->assertSame($expected, $data); } @@ -54,7 +54,7 @@ public function testConvertDataToDB(array $types, array $phpData, array $expecte { $converter = $this->createDataConverter($types); - $data = $converter->toDatabase($phpData); + $data = $converter->toDataSource($phpData); $this->assertSame($expected, $data); } @@ -335,7 +335,7 @@ public function testDateTimeConvertDataFromDB(): void 'id' => '1', 'date' => '2023-11-18 14:18:18', ]; - $data = $converter->fromDatabase($dbData); + $data = $converter->fromDataSource($dbData); $this->assertInstanceOf(Time::class, $data['date']); $expectedDate = Time::parse('2023-11-18 14:18:18'); @@ -354,7 +354,7 @@ public function testDateTimeConvertDataToDB(): void 'id' => '1', 'date' => Time::parse('2023-11-18 14:18:18'), ]; - $data = $converter->toDatabase($phpData); + $data = $converter->toDataSource($phpData); $this->assertSame('2023-11-18 14:18:18', $data['date']); } @@ -371,7 +371,7 @@ public function testTimestampConvertDataFromDB(): void 'id' => '1', 'date' => '1700285831', ]; - $data = $converter->fromDatabase($dbData); + $data = $converter->fromDataSource($dbData); $this->assertInstanceOf(Time::class, $data['date']); $this->assertSame(1_700_285_831, $data['date']->getTimestamp()); @@ -389,7 +389,7 @@ public function testTimestampConvertDataToDB(): void 'id' => '1', 'date' => Time::createFromTimestamp(1_700_285_831), ]; - $data = $converter->toDatabase($phpData); + $data = $converter->toDataSource($phpData); $this->assertSame(1_700_285_831, $data['date']); } @@ -406,7 +406,7 @@ public function testURIConvertDataFromDB(): void 'id' => '1', 'url' => 'http://example.com/', ]; - $data = $converter->fromDatabase($dbData); + $data = $converter->fromDataSource($dbData); $this->assertInstanceOf(URI::class, $data['url']); $this->assertSame('http://example.com/', (string) $data['url']); @@ -424,7 +424,7 @@ public function testURIConvertDataToDB(): void 'id' => '1', 'url' => new URI('http://example.com/'), ]; - $data = $converter->toDatabase($phpData); + $data = $converter->toDataSource($phpData); $this->assertSame('http://example.com/', $data['url']); } @@ -444,7 +444,7 @@ public function testInvalidType(): void 'id' => '1', 'remark' => '{"foo":"bar", "baz":true}', ]; - $converter->fromDatabase($dbData); + $converter->fromDataSource($dbData); } public function testInvalidValue(): void @@ -464,7 +464,7 @@ public function testInvalidValue(): void 'id' => '1', 'remark' => true, ]; - $converter->fromDatabase($dbData); + $converter->fromDataSource($dbData); } public function testInvalidCastHandler(): void @@ -484,7 +484,7 @@ public function testInvalidCastHandler(): void 'id' => '1', 'remark' => '{"foo":"bar", "baz":true}', ]; - $converter->fromDatabase($dbData); + $converter->fromDataSource($dbData); } private function createDataConverter(array $types, array $handlers = []): DataConverter From 938ae6eb5159f9380258d09112f7ec53f4737567 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 10:15:51 +0900 Subject: [PATCH 18/38] refactor: change namespace to CodeIgniter\DataConverter --- deptrac.yaml | 11 ++++++-- .../DataConverter/Cast/ArrayCast.php | 2 +- .../DataConverter/Cast/BaseCast.php | 2 +- .../DataConverter/Cast/BooleanCast.php | 2 +- .../DataConverter/Cast/CSVCast.php | 2 +- .../DataConverter/Cast/CastInterface.php | 2 +- .../DataConverter/Cast/DatetimeCast.php | 2 +- .../DataConverter/Cast/FloatCast.php | 2 +- .../DataConverter/Cast/IntBoolCast.php | 2 +- .../DataConverter/Cast/IntegerCast.php | 2 +- .../DataConverter/Cast/JsonCast.php | 4 +-- .../DataConverter/Cast/TimestampCast.php | 2 +- .../DataConverter/Cast/URICast.php | 2 +- .../DataConverter/DataConverter.php | 28 +++++++++---------- .../Exceptions/CastException.php | 2 +- .../DataConverter/DataConverterTest.php | 6 ++-- 16 files changed, 40 insertions(+), 33 deletions(-) rename system/{Database => }/DataConverter/Cast/ArrayCast.php (95%) rename system/{Database => }/DataConverter/Cast/BaseCast.php (95%) rename system/{Database => }/DataConverter/Cast/BooleanCast.php (94%) rename system/{Database => }/DataConverter/Cast/CSVCast.php (94%) rename system/{Database => }/DataConverter/Cast/CastInterface.php (95%) rename system/{Database => }/DataConverter/Cast/DatetimeCast.php (95%) rename system/{Database => }/DataConverter/Cast/FloatCast.php (93%) rename system/{Database => }/DataConverter/Cast/IntBoolCast.php (94%) rename system/{Database => }/DataConverter/Cast/IntegerCast.php (93%) rename system/{Database => }/DataConverter/Cast/JsonCast.php (92%) rename system/{Database => }/DataConverter/Cast/TimestampCast.php (95%) rename system/{Database => }/DataConverter/Cast/URICast.php (94%) rename system/{Database => }/DataConverter/DataConverter.php (85%) rename system/{Database => }/DataConverter/Exceptions/CastException.php (88%) rename tests/system/{Database => }/DataConverter/DataConverterTest.php (98%) diff --git a/deptrac.yaml b/deptrac.yaml index 49e81587539f..8aea2c5dd38e 100644 --- a/deptrac.yaml +++ b/deptrac.yaml @@ -35,6 +35,10 @@ parameters: collectors: - type: className regex: ^Codeigniter\\Database\\.* + - name: DataConverter + collectors: + - type: className + regex: ^Codeigniter\\DataConverter\\.* - name: Email collectors: - type: className @@ -166,6 +170,9 @@ parameters: - Entity - Events - I18n + DataConverter: + - I18n + - URI Email: - I18n - Events @@ -228,8 +235,8 @@ parameters: - CodeIgniter\HTTP\Header - CodeIgniter\HTTP\IncomingRequest - CodeIgniter\HTTP\ResponseInterface - CodeIgniter\Database\DataConverter\Cast\URICast: - - CodeIgniter\HTTP\URI + CodeIgniter\DataConverter\Exceptions\CastException: + - CodeIgniter\Entity\Exceptions\CastException CodeIgniter\Entity\Cast\URICast: - CodeIgniter\HTTP\URI CodeIgniter\Log\Handlers\ChromeLoggerHandler: diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/DataConverter/Cast/ArrayCast.php similarity index 95% rename from system/Database/DataConverter/Cast/ArrayCast.php rename to system/DataConverter/Cast/ArrayCast.php index 9375e00bef84..c207a9492978 100644 --- a/system/Database/DataConverter/Cast/ArrayCast.php +++ b/system/DataConverter/Cast/ArrayCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * Class ArrayCast diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/DataConverter/Cast/BaseCast.php similarity index 95% rename from system/Database/DataConverter/Cast/BaseCast.php rename to system/DataConverter/Cast/BaseCast.php index 121b27cfc8af..0de3884d5319 100644 --- a/system/Database/DataConverter/Cast/BaseCast.php +++ b/system/DataConverter/Cast/BaseCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; use TypeError; diff --git a/system/Database/DataConverter/Cast/BooleanCast.php b/system/DataConverter/Cast/BooleanCast.php similarity index 94% rename from system/Database/DataConverter/Cast/BooleanCast.php rename to system/DataConverter/Cast/BooleanCast.php index 3943cab8ae52..2e802e7c8bc3 100644 --- a/system/Database/DataConverter/Cast/BooleanCast.php +++ b/system/DataConverter/Cast/BooleanCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * Class BooleanCast diff --git a/system/Database/DataConverter/Cast/CSVCast.php b/system/DataConverter/Cast/CSVCast.php similarity index 94% rename from system/Database/DataConverter/Cast/CSVCast.php rename to system/DataConverter/Cast/CSVCast.php index 3eb5538a0ea9..36a045ed942c 100644 --- a/system/Database/DataConverter/Cast/CSVCast.php +++ b/system/DataConverter/Cast/CSVCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * Class CSVCast diff --git a/system/Database/DataConverter/Cast/CastInterface.php b/system/DataConverter/Cast/CastInterface.php similarity index 95% rename from system/Database/DataConverter/Cast/CastInterface.php rename to system/DataConverter/Cast/CastInterface.php index fb79752b5ba7..32290ee79518 100644 --- a/system/Database/DataConverter/Cast/CastInterface.php +++ b/system/DataConverter/Cast/CastInterface.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * @template TPhpValue PHP data type diff --git a/system/Database/DataConverter/Cast/DatetimeCast.php b/system/DataConverter/Cast/DatetimeCast.php similarity index 95% rename from system/Database/DataConverter/Cast/DatetimeCast.php rename to system/DataConverter/Cast/DatetimeCast.php index fea1ae687d33..750d7c8117fe 100644 --- a/system/Database/DataConverter/Cast/DatetimeCast.php +++ b/system/DataConverter/Cast/DatetimeCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; use CodeIgniter\I18n\Time; diff --git a/system/Database/DataConverter/Cast/FloatCast.php b/system/DataConverter/Cast/FloatCast.php similarity index 93% rename from system/Database/DataConverter/Cast/FloatCast.php rename to system/DataConverter/Cast/FloatCast.php index d19c4b840d8e..72552746548f 100644 --- a/system/Database/DataConverter/Cast/FloatCast.php +++ b/system/DataConverter/Cast/FloatCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * Class FloatCast diff --git a/system/Database/DataConverter/Cast/IntBoolCast.php b/system/DataConverter/Cast/IntBoolCast.php similarity index 94% rename from system/Database/DataConverter/Cast/IntBoolCast.php rename to system/DataConverter/Cast/IntBoolCast.php index dc3642371aab..9d05682fcfe7 100644 --- a/system/Database/DataConverter/Cast/IntBoolCast.php +++ b/system/DataConverter/Cast/IntBoolCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * Int Bool Cast diff --git a/system/Database/DataConverter/Cast/IntegerCast.php b/system/DataConverter/Cast/IntegerCast.php similarity index 93% rename from system/Database/DataConverter/Cast/IntegerCast.php rename to system/DataConverter/Cast/IntegerCast.php index 04ef5596bc23..7207a18e3330 100644 --- a/system/Database/DataConverter/Cast/IntegerCast.php +++ b/system/DataConverter/Cast/IntegerCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; /** * Class IntegerCast diff --git a/system/Database/DataConverter/Cast/JsonCast.php b/system/DataConverter/Cast/JsonCast.php similarity index 92% rename from system/Database/DataConverter/Cast/JsonCast.php rename to system/DataConverter/Cast/JsonCast.php index a7ded03e7c7f..a73f3d85e6ed 100644 --- a/system/Database/DataConverter/Cast/JsonCast.php +++ b/system/DataConverter/Cast/JsonCast.php @@ -9,9 +9,9 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; -use CodeIgniter\Database\DataConverter\Exceptions\CastException; +use CodeIgniter\DataConverter\Exceptions\CastException; use JsonException; use stdClass; diff --git a/system/Database/DataConverter/Cast/TimestampCast.php b/system/DataConverter/Cast/TimestampCast.php similarity index 95% rename from system/Database/DataConverter/Cast/TimestampCast.php rename to system/DataConverter/Cast/TimestampCast.php index 26fb45df2f39..32914da59d78 100644 --- a/system/Database/DataConverter/Cast/TimestampCast.php +++ b/system/DataConverter/Cast/TimestampCast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; use CodeIgniter\I18n\Time; diff --git a/system/Database/DataConverter/Cast/URICast.php b/system/DataConverter/Cast/URICast.php similarity index 94% rename from system/Database/DataConverter/Cast/URICast.php rename to system/DataConverter/Cast/URICast.php index b5c9f237b2d9..6f7e35918ebc 100644 --- a/system/Database/DataConverter/Cast/URICast.php +++ b/system/DataConverter/Cast/URICast.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Cast; +namespace CodeIgniter\DataConverter\Cast; use CodeIgniter\HTTP\URI; diff --git a/system/Database/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php similarity index 85% rename from system/Database/DataConverter/DataConverter.php rename to system/DataConverter/DataConverter.php index 409ad0ad0215..dfef63d1994e 100644 --- a/system/Database/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -9,25 +9,25 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter; - -use CodeIgniter\Database\DataConverter\Cast\ArrayCast; -use CodeIgniter\Database\DataConverter\Cast\BooleanCast; -use CodeIgniter\Database\DataConverter\Cast\CastInterface; -use CodeIgniter\Database\DataConverter\Cast\CSVCast; -use CodeIgniter\Database\DataConverter\Cast\DatetimeCast; -use CodeIgniter\Database\DataConverter\Cast\FloatCast; -use CodeIgniter\Database\DataConverter\Cast\IntBoolCast; -use CodeIgniter\Database\DataConverter\Cast\IntegerCast; -use CodeIgniter\Database\DataConverter\Cast\JsonCast; -use CodeIgniter\Database\DataConverter\Cast\TimestampCast; -use CodeIgniter\Database\DataConverter\Cast\URICast; +namespace CodeIgniter\DataConverter; + +use CodeIgniter\DataConverter\Cast\ArrayCast; +use CodeIgniter\DataConverter\Cast\BooleanCast; +use CodeIgniter\DataConverter\Cast\CastInterface; +use CodeIgniter\DataConverter\Cast\CSVCast; +use CodeIgniter\DataConverter\Cast\DatetimeCast; +use CodeIgniter\DataConverter\Cast\FloatCast; +use CodeIgniter\DataConverter\Cast\IntBoolCast; +use CodeIgniter\DataConverter\Cast\IntegerCast; +use CodeIgniter\DataConverter\Cast\JsonCast; +use CodeIgniter\DataConverter\Cast\TimestampCast; +use CodeIgniter\DataConverter\Cast\URICast; use InvalidArgumentException; /** * PHP data <==> DataSource data converter * - * @see \CodeIgniter\Database\DataConverter\DataConverterTest + * @see \CodeIgniter\DataConverter\DataConverterTest */ class DataConverter { diff --git a/system/Database/DataConverter/Exceptions/CastException.php b/system/DataConverter/Exceptions/CastException.php similarity index 88% rename from system/Database/DataConverter/Exceptions/CastException.php rename to system/DataConverter/Exceptions/CastException.php index a5576c822c70..139bd7acd863 100644 --- a/system/Database/DataConverter/Exceptions/CastException.php +++ b/system/DataConverter/Exceptions/CastException.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter\Exceptions; +namespace CodeIgniter\DataConverter\Exceptions; use CodeIgniter\Entity\Exceptions\CastException as EntityCastException; diff --git a/tests/system/Database/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php similarity index 98% rename from tests/system/Database/DataConverter/DataConverterTest.php rename to tests/system/DataConverter/DataConverterTest.php index 7dc89d2dbe7a..4cdb55165fa1 100644 --- a/tests/system/Database/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\Database\DataConverter; +namespace CodeIgniter\DataConverter; use CodeIgniter\HTTP\URI; use CodeIgniter\I18n\Time; @@ -451,7 +451,7 @@ public function testInvalidValue(): void { $this->expectException(TypeError::class); $this->expectExceptionMessage( - '[CodeIgniter\Database\DataConverter\Cast\JsonCast] Invalid value type: bool, and its value: true' + '[CodeIgniter\DataConverter\Cast\JsonCast] Invalid value type: bool, and its value: true' ); $types = [ @@ -471,7 +471,7 @@ public function testInvalidCastHandler(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( - 'Invalid class type. It must implement CastInterface. class: CodeIgniter\Database\DataConverter\DataConverter' + 'Invalid class type. It must implement CastInterface. class: CodeIgniter\DataConverter\DataConverter' ); $types = [ From e42704db50db949feb72729d2af532427d72b789 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 10:57:27 +0900 Subject: [PATCH 19/38] feat: datetime supports format --- system/DataConverter/Cast/DatetimeCast.php | 7 ++++++- .../DataConverter/DataConverterTest.php | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/system/DataConverter/Cast/DatetimeCast.php b/system/DataConverter/Cast/DatetimeCast.php index 750d7c8117fe..13463e7702ad 100644 --- a/system/DataConverter/Cast/DatetimeCast.php +++ b/system/DataConverter/Cast/DatetimeCast.php @@ -29,7 +29,12 @@ public static function fromDataSource(mixed $value, array $params = []): Time self::invalidTypeValueError($value); } - return Time::parse($value); + /** + * @see https://www.php.net/manual/en/datetimeimmutable.createfromformat.php#datetimeimmutable.createfromformat.parameters + */ + $format = $params[0] ?? 'Y-m-d H:i:s'; + + return Time::createFromFormat($format, $value); } public static function toDataSource(mixed $value, array $params = []): string diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index 4cdb55165fa1..1e5906bc3a36 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -342,6 +342,25 @@ public function testDateTimeConvertDataFromDB(): void $this->assertSame($expectedDate->getTimestamp(), $data['date']->getTimestamp()); } + public function testDateTimeConvertDataFromDBWithFormat(): void + { + $types = [ + 'id' => 'int', + 'date' => 'datetime[j-M-Y]', + ]; + $converter = $this->createDataConverter($types); + + $dbData = [ + 'id' => '1', + 'date' => '15-Feb-2009', + ]; + $data = $converter->fromDataSource($dbData); + + $this->assertInstanceOf(Time::class, $data['date']); + $expectedDate = Time::createFromFormat('j-M-Y', '15-Feb-2009'); + $this->assertSame($expectedDate->getTimestamp(), $data['date']->getTimestamp()); + } + public function testDateTimeConvertDataToDB(): void { $types = [ From 7e4049e916301c25335a0733f194e4859179e229 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 13:27:10 +0900 Subject: [PATCH 20/38] refactor: rename variable names --- system/DataConverter/DataConverter.php | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index dfef63d1994e..29b26efe7a83 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -76,27 +76,31 @@ public function __construct( /** * Converts data from DataSource to PHP array with specified type values. + * + * @param array $data DataSource data */ - public function fromDataSource(array $dbData): array + public function fromDataSource(array $data): array { $output = []; - foreach ($dbData as $column => $value) { - $output[$column] = $this->castAs($value, $column, 'fromDataSource'); + foreach ($data as $field => $value) { + $output[$field] = $this->castAs($value, $field, 'fromDataSource'); } return $output; } /** - * Converts PHP array to data for DataSource column types. + * Converts PHP array to data for DataSource field types. + * + * @param array $phpData PHP data */ public function toDataSource(array $phpData): array { $output = []; - foreach ($phpData as $column => $value) { - $output[$column] = $this->castAs($value, $column, 'toDataSource'); + foreach ($phpData as $field => $value) { + $output[$field] = $this->castAs($value, $field, 'toDataSource'); } return $output; @@ -108,18 +112,18 @@ public function toDataSource(array $phpData): array * instead of casting $value if ($value === null). * * @param mixed $value The value to convert - * @param string $column The column name + * @param string $field The field name * @param string $method Allowed to "fromDataSource" and "toDataSource" * @phpstan-param 'fromDataSource'|'toDataSource' $method */ - protected function castAs($value, string $column, string $method = 'fromDataSource'): mixed + protected function castAs($value, string $field, string $method = 'fromDataSource'): mixed { // If the type is not defined, return as it is. - if (! isset($this->types[$column])) { + if (! isset($this->types[$field])) { return $value; } - $type = $this->types[$column]; + $type = $this->types[$field]; $isNullable = false; @@ -155,7 +159,7 @@ protected function castAs($value, string $column, string $method = 'fromDataSour $handlers = array_merge($this->defaultCastHandlers, $this->castHandlers); if (! isset($handlers[$type])) { - throw new InvalidArgumentException('No such handler for "' . $column . '". Invalid type: ' . $type); + throw new InvalidArgumentException('No such handler for "' . $field . '". Invalid type: ' . $type); } $handler = $handlers[$type]; From 4cafb96dcf8947910f0edd2e191506b757fd2263 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 24 Nov 2023 13:40:41 +0900 Subject: [PATCH 21/38] feat: add nullable check --- system/DataConverter/DataConverter.php | 6 ++++++ .../system/DataConverter/DataConverterTest.php | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index 29b26efe7a83..c7a4f7d27c87 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -23,6 +23,7 @@ use CodeIgniter\DataConverter\Cast\TimestampCast; use CodeIgniter\DataConverter\Cast\URICast; use InvalidArgumentException; +use TypeError; /** * PHP data <==> DataSource data converter @@ -127,6 +128,7 @@ protected function castAs($value, string $field, string $method = 'fromDataSourc $isNullable = false; + // Is nullable? if (str_starts_with($type, '?')) { $isNullable = true; @@ -135,6 +137,10 @@ protected function castAs($value, string $field, string $method = 'fromDataSourc } $type = substr($type, 1); + } elseif ($value === null) { + $message = 'Field "' . $field . '" is not nullable, but null was passed.'; + + throw new TypeError($message); } // In order not to create a separate handler for the diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index 1e5906bc3a36..2664cf7cefe6 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -506,6 +506,24 @@ public function testInvalidCastHandler(): void $converter->fromDataSource($dbData); } + public function testNotNullable(): void + { + $this->expectException(TypeError::class); + $this->expectExceptionMessage('Field "remark" is not nullable, but null was passed.'); + + $types = [ + 'id' => 'int', + 'remark' => 'json-array', + ]; + $converter = $this->createDataConverter($types); + + $dbData = [ + 'id' => 1, + 'remark' => null, + ]; + $converter->toDataSource($dbData); + } + private function createDataConverter(array $types, array $handlers = []): DataConverter { return new DataConverter($types, $handlers); From 896cca5ea9ae734e75038ef45a59b0208e38481b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 26 Nov 2023 17:05:12 +0900 Subject: [PATCH 22/38] chore: vendor/bin/psalm --set-baseline=psalm-baseline.xml --- psalm-baseline.xml | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 6b648e972008..4ced4dd7a851 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + ]]> @@ -80,22 +80,43 @@ ];]]> - - - db->transStatus]]> - - - + CastInterface - + + + $value + $value + + + TDbColumn TPhpValue + + + $value + + + + + $value + + + + + $value + + + + + db->transStatus]]> + + OCI_COMMIT_ON_SUCCESS From c7b6760321d7adac69a107fe3249556345249ce5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 7 Dec 2023 19:59:08 +0900 Subject: [PATCH 23/38] refactor: add `declare(strict_types=1)` --- system/DataConverter/Cast/ArrayCast.php | 2 ++ system/DataConverter/Cast/BaseCast.php | 2 ++ system/DataConverter/Cast/BooleanCast.php | 2 ++ system/DataConverter/Cast/CSVCast.php | 2 ++ system/DataConverter/Cast/CastInterface.php | 2 ++ system/DataConverter/Cast/DatetimeCast.php | 2 ++ system/DataConverter/Cast/FloatCast.php | 2 ++ system/DataConverter/Cast/IntBoolCast.php | 2 ++ system/DataConverter/Cast/IntegerCast.php | 2 ++ system/DataConverter/Cast/JsonCast.php | 2 ++ system/DataConverter/Cast/TimestampCast.php | 2 ++ system/DataConverter/Cast/URICast.php | 2 ++ system/DataConverter/DataConverter.php | 2 ++ system/DataConverter/Exceptions/CastException.php | 2 ++ tests/system/DataConverter/DataConverterTest.php | 2 ++ 15 files changed, 30 insertions(+) diff --git a/system/DataConverter/Cast/ArrayCast.php b/system/DataConverter/Cast/ArrayCast.php index c207a9492978..7d9381cf5298 100644 --- a/system/DataConverter/Cast/ArrayCast.php +++ b/system/DataConverter/Cast/ArrayCast.php @@ -1,5 +1,7 @@ Date: Thu, 28 Dec 2023 09:05:22 +0900 Subject: [PATCH 24/38] docs: improve comments --- system/DataConverter/Cast/CastInterface.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/DataConverter/Cast/CastInterface.php b/system/DataConverter/Cast/CastInterface.php index db10c58102df..6c611eef2ad0 100644 --- a/system/DataConverter/Cast/CastInterface.php +++ b/system/DataConverter/Cast/CastInterface.php @@ -14,7 +14,7 @@ namespace CodeIgniter\DataConverter\Cast; /** - * @template TPhpValue PHP data type + * @template TPhpValue PHP native value type * @template TToDb Data type to pass to database driver * @template TDbColumn Data type from database driver */ @@ -26,14 +26,14 @@ interface CastInterface * @param TDbColumn $value Data from database driver * @param list $params Additional param * - * @return TPhpValue + * @return TPhpValue PHP native value */ public static function fromDataSource(mixed $value, array $params = []): mixed; /** * Takes the PHP value, returns its value for DataSource. * - * @param TPhpValue $value PHP data + * @param TPhpValue $value PHP native value * @param list $params Additional param * * @return TToDb Data to pass to database driver From 38a88c9ca0cde161ef9ad8a7b101bfb135d334b3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 13:31:38 +0900 Subject: [PATCH 25/38] docs: update comments in Entity/Cast/CastInterface --- system/Entity/Cast/CastInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Entity/Cast/CastInterface.php b/system/Entity/Cast/CastInterface.php index 1820bb45d886..0a17ec0861e0 100644 --- a/system/Entity/Cast/CastInterface.php +++ b/system/Entity/Cast/CastInterface.php @@ -19,7 +19,7 @@ interface CastInterface { /** - * Get + * Takes a value from Database, returns its value for PHP. * * @param array|bool|float|int|object|string|null $value Data * @param array $params Additional param @@ -29,7 +29,7 @@ interface CastInterface public static function get($value, array $params = []); /** - * Set + * Takes a PHP value, returns its value for Database. * * @param array|bool|float|int|object|string|null $value Data * @param array $params Additional param From f2417d3a2e7a546889d54f8a365e255603e961ab Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 13:32:08 +0900 Subject: [PATCH 26/38] refactor: make method names to be same as Entity CastInterface --- system/DataConverter/Cast/ArrayCast.php | 4 ++-- system/DataConverter/Cast/BaseCast.php | 4 ++-- system/DataConverter/Cast/BooleanCast.php | 2 +- system/DataConverter/Cast/CSVCast.php | 4 ++-- system/DataConverter/Cast/CastInterface.php | 4 ++-- system/DataConverter/Cast/DatetimeCast.php | 4 ++-- system/DataConverter/Cast/FloatCast.php | 2 +- system/DataConverter/Cast/IntBoolCast.php | 4 ++-- system/DataConverter/Cast/IntegerCast.php | 2 +- system/DataConverter/Cast/JsonCast.php | 4 ++-- system/DataConverter/Cast/TimestampCast.php | 4 ++-- system/DataConverter/Cast/URICast.php | 4 ++-- system/DataConverter/DataConverter.php | 10 +++++----- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/system/DataConverter/Cast/ArrayCast.php b/system/DataConverter/Cast/ArrayCast.php index 7d9381cf5298..d87b5e49ee04 100644 --- a/system/DataConverter/Cast/ArrayCast.php +++ b/system/DataConverter/Cast/ArrayCast.php @@ -23,7 +23,7 @@ */ class ArrayCast extends BaseCast implements CastInterface { - public static function fromDataSource(mixed $value, array $params = []): array + public static function get(mixed $value, array $params = []): array { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -36,7 +36,7 @@ public static function fromDataSource(mixed $value, array $params = []): array return (array) $value; } - public static function toDataSource(mixed $value, array $params = []): string + public static function set(mixed $value, array $params = []): string { return serialize($value); } diff --git a/system/DataConverter/Cast/BaseCast.php b/system/DataConverter/Cast/BaseCast.php index 947bf8ab5815..ee96b2599565 100644 --- a/system/DataConverter/Cast/BaseCast.php +++ b/system/DataConverter/Cast/BaseCast.php @@ -24,12 +24,12 @@ */ abstract class BaseCast implements CastInterface { - public static function fromDataSource(mixed $value, array $params = []): mixed + public static function get(mixed $value, array $params = []): mixed { return $value; } - public static function toDataSource(mixed $value, array $params = []): mixed + public static function set(mixed $value, array $params = []): mixed { return $value; } diff --git a/system/DataConverter/Cast/BooleanCast.php b/system/DataConverter/Cast/BooleanCast.php index 9a3a997e3254..01b8e34ec4e0 100644 --- a/system/DataConverter/Cast/BooleanCast.php +++ b/system/DataConverter/Cast/BooleanCast.php @@ -23,7 +23,7 @@ */ class BooleanCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): bool + public static function get(mixed $value, array $params = []): bool { // For PostgreSQL if ($value === 't') { diff --git a/system/DataConverter/Cast/CSVCast.php b/system/DataConverter/Cast/CSVCast.php index f3154b3f1289..89283dce2012 100644 --- a/system/DataConverter/Cast/CSVCast.php +++ b/system/DataConverter/Cast/CSVCast.php @@ -23,7 +23,7 @@ */ class CSVCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): array + public static function get(mixed $value, array $params = []): array { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -32,7 +32,7 @@ public static function fromDataSource(mixed $value, array $params = []): array return explode(',', $value); } - public static function toDataSource(mixed $value, array $params = []): string + public static function set(mixed $value, array $params = []): string { if (! is_array($value)) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/Cast/CastInterface.php b/system/DataConverter/Cast/CastInterface.php index 6c611eef2ad0..454d3c271593 100644 --- a/system/DataConverter/Cast/CastInterface.php +++ b/system/DataConverter/Cast/CastInterface.php @@ -28,7 +28,7 @@ interface CastInterface * * @return TPhpValue PHP native value */ - public static function fromDataSource(mixed $value, array $params = []): mixed; + public static function get(mixed $value, array $params = []): mixed; /** * Takes the PHP value, returns its value for DataSource. @@ -38,5 +38,5 @@ public static function fromDataSource(mixed $value, array $params = []): mixed; * * @return TToDb Data to pass to database driver */ - public static function toDataSource(mixed $value, array $params = []): mixed; + public static function set(mixed $value, array $params = []): mixed; } diff --git a/system/DataConverter/Cast/DatetimeCast.php b/system/DataConverter/Cast/DatetimeCast.php index fe62409cc00b..b2d06c5cb299 100644 --- a/system/DataConverter/Cast/DatetimeCast.php +++ b/system/DataConverter/Cast/DatetimeCast.php @@ -25,7 +25,7 @@ */ class DatetimeCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): Time + public static function get(mixed $value, array $params = []): Time { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -39,7 +39,7 @@ public static function fromDataSource(mixed $value, array $params = []): Time return Time::createFromFormat($format, $value); } - public static function toDataSource(mixed $value, array $params = []): string + public static function set(mixed $value, array $params = []): string { if (! $value instanceof Time) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/Cast/FloatCast.php b/system/DataConverter/Cast/FloatCast.php index 7d81cdced5b9..86c500de9dfc 100644 --- a/system/DataConverter/Cast/FloatCast.php +++ b/system/DataConverter/Cast/FloatCast.php @@ -23,7 +23,7 @@ */ class FloatCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): float + public static function get(mixed $value, array $params = []): float { if (! is_float($value) && ! is_string($value)) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/Cast/IntBoolCast.php b/system/DataConverter/Cast/IntBoolCast.php index 7130dd4b225b..e5badd00fc98 100644 --- a/system/DataConverter/Cast/IntBoolCast.php +++ b/system/DataConverter/Cast/IntBoolCast.php @@ -23,7 +23,7 @@ */ final class IntBoolCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): bool + public static function get(mixed $value, array $params = []): bool { if (! is_int($value) && ! is_string($value)) { self::invalidTypeValueError($value); @@ -32,7 +32,7 @@ public static function fromDataSource(mixed $value, array $params = []): bool return (bool) $value; } - public static function toDataSource(mixed $value, array $params = []): int + public static function set(mixed $value, array $params = []): int { if (! is_bool($value)) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/Cast/IntegerCast.php b/system/DataConverter/Cast/IntegerCast.php index e596e8b83f30..f0cad1ce0ef7 100644 --- a/system/DataConverter/Cast/IntegerCast.php +++ b/system/DataConverter/Cast/IntegerCast.php @@ -23,7 +23,7 @@ */ class IntegerCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): int + public static function get(mixed $value, array $params = []): int { if (! is_string($value) && ! is_int($value)) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/Cast/JsonCast.php b/system/DataConverter/Cast/JsonCast.php index 020f7c6f5c28..fdedff4c3820 100644 --- a/system/DataConverter/Cast/JsonCast.php +++ b/system/DataConverter/Cast/JsonCast.php @@ -27,7 +27,7 @@ */ class JsonCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): array|stdClass + public static function get(mixed $value, array $params = []): array|stdClass { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -46,7 +46,7 @@ public static function fromDataSource(mixed $value, array $params = []): array|s return $output; } - public static function toDataSource(mixed $value, array $params = []): string + public static function set(mixed $value, array $params = []): string { try { $output = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); diff --git a/system/DataConverter/Cast/TimestampCast.php b/system/DataConverter/Cast/TimestampCast.php index eb0d77490b47..7d480e891a36 100644 --- a/system/DataConverter/Cast/TimestampCast.php +++ b/system/DataConverter/Cast/TimestampCast.php @@ -25,7 +25,7 @@ */ class TimestampCast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): Time + public static function get(mixed $value, array $params = []): Time { if (! is_int($value) && ! is_string($value)) { self::invalidTypeValueError($value); @@ -34,7 +34,7 @@ public static function fromDataSource(mixed $value, array $params = []): Time return Time::createFromTimestamp((int) $value); } - public static function toDataSource(mixed $value, array $params = []): int + public static function set(mixed $value, array $params = []): int { if (! $value instanceof Time) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/Cast/URICast.php b/system/DataConverter/Cast/URICast.php index 11d894e260d4..4f6a5284b971 100644 --- a/system/DataConverter/Cast/URICast.php +++ b/system/DataConverter/Cast/URICast.php @@ -25,7 +25,7 @@ */ class URICast extends BaseCast { - public static function fromDataSource(mixed $value, array $params = []): URI + public static function get(mixed $value, array $params = []): URI { if (! is_string($value)) { self::invalidTypeValueError($value); @@ -34,7 +34,7 @@ public static function fromDataSource(mixed $value, array $params = []): URI return new URI($value); } - public static function toDataSource(mixed $value, array $params = []): string + public static function set(mixed $value, array $params = []): string { if (! $value instanceof URI) { self::invalidTypeValueError($value); diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index 51eaa4b5a3d0..9ee50ad94753 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -87,7 +87,7 @@ public function fromDataSource(array $data): array $output = []; foreach ($data as $field => $value) { - $output[$field] = $this->castAs($value, $field, 'fromDataSource'); + $output[$field] = $this->castAs($value, $field, 'get'); } return $output; @@ -103,7 +103,7 @@ public function toDataSource(array $phpData): array $output = []; foreach ($phpData as $field => $value) { - $output[$field] = $this->castAs($value, $field, 'toDataSource'); + $output[$field] = $this->castAs($value, $field, 'set'); } return $output; @@ -116,10 +116,10 @@ public function toDataSource(array $phpData): array * * @param mixed $value The value to convert * @param string $field The field name - * @param string $method Allowed to "fromDataSource" and "toDataSource" - * @phpstan-param 'fromDataSource'|'toDataSource' $method + * @param string $method Allowed to "get" and "set" + * @phpstan-param 'get'|'set' $method */ - protected function castAs($value, string $field, string $method = 'fromDataSource'): mixed + protected function castAs($value, string $field, string $method = 'get'): mixed { // If the type is not defined, return as it is. if (! isset($this->types[$field])) { From 8200f8efce4c8eae53b471dee62054db7bb573ec Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 13:43:41 +0900 Subject: [PATCH 27/38] refactor: move DataConverter/Cast to DataCaster/Cast --- .../Cast/ArrayCast.php | 2 +- .../Cast/BaseCast.php | 2 +- .../Cast/BooleanCast.php | 2 +- .../Cast/CSVCast.php | 2 +- .../Cast/CastInterface.php | 2 +- .../Cast/DatetimeCast.php | 2 +- .../Cast/FloatCast.php | 2 +- .../Cast/IntBoolCast.php | 2 +- .../Cast/IntegerCast.php | 2 +- .../Cast/JsonCast.php | 4 ++-- .../Cast/TimestampCast.php | 2 +- .../Cast/URICast.php | 2 +- .../Exceptions/CastException.php | 2 +- system/DataConverter/DataConverter.php | 22 +++++++++---------- .../DataConverter/DataConverterTest.php | 2 +- 15 files changed, 26 insertions(+), 26 deletions(-) rename system/{DataConverter => DataCaster}/Cast/ArrayCast.php (96%) rename system/{DataConverter => DataCaster}/Cast/BaseCast.php (96%) rename system/{DataConverter => DataCaster}/Cast/BooleanCast.php (95%) rename system/{DataConverter => DataCaster}/Cast/CSVCast.php (95%) rename system/{DataConverter => DataCaster}/Cast/CastInterface.php (96%) rename system/{DataConverter => DataCaster}/Cast/DatetimeCast.php (96%) rename system/{DataConverter => DataCaster}/Cast/FloatCast.php (94%) rename system/{DataConverter => DataCaster}/Cast/IntBoolCast.php (95%) rename system/{DataConverter => DataCaster}/Cast/IntegerCast.php (94%) rename system/{DataConverter => DataCaster}/Cast/JsonCast.php (93%) rename system/{DataConverter => DataCaster}/Cast/TimestampCast.php (96%) rename system/{DataConverter => DataCaster}/Cast/URICast.php (95%) rename system/{DataConverter => DataCaster}/Exceptions/CastException.php (91%) diff --git a/system/DataConverter/Cast/ArrayCast.php b/system/DataCaster/Cast/ArrayCast.php similarity index 96% rename from system/DataConverter/Cast/ArrayCast.php rename to system/DataCaster/Cast/ArrayCast.php index d87b5e49ee04..c13d73231ddf 100644 --- a/system/DataConverter/Cast/ArrayCast.php +++ b/system/DataCaster/Cast/ArrayCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * Class ArrayCast diff --git a/system/DataConverter/Cast/BaseCast.php b/system/DataCaster/Cast/BaseCast.php similarity index 96% rename from system/DataConverter/Cast/BaseCast.php rename to system/DataCaster/Cast/BaseCast.php index ee96b2599565..3b4a5403d7da 100644 --- a/system/DataConverter/Cast/BaseCast.php +++ b/system/DataCaster/Cast/BaseCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; use TypeError; diff --git a/system/DataConverter/Cast/BooleanCast.php b/system/DataCaster/Cast/BooleanCast.php similarity index 95% rename from system/DataConverter/Cast/BooleanCast.php rename to system/DataCaster/Cast/BooleanCast.php index 01b8e34ec4e0..07ebea2041ed 100644 --- a/system/DataConverter/Cast/BooleanCast.php +++ b/system/DataCaster/Cast/BooleanCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * Class BooleanCast diff --git a/system/DataConverter/Cast/CSVCast.php b/system/DataCaster/Cast/CSVCast.php similarity index 95% rename from system/DataConverter/Cast/CSVCast.php rename to system/DataCaster/Cast/CSVCast.php index 89283dce2012..a834068a9512 100644 --- a/system/DataConverter/Cast/CSVCast.php +++ b/system/DataCaster/Cast/CSVCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * Class CSVCast diff --git a/system/DataConverter/Cast/CastInterface.php b/system/DataCaster/Cast/CastInterface.php similarity index 96% rename from system/DataConverter/Cast/CastInterface.php rename to system/DataCaster/Cast/CastInterface.php index 454d3c271593..f8a11b0c2376 100644 --- a/system/DataConverter/Cast/CastInterface.php +++ b/system/DataCaster/Cast/CastInterface.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * @template TPhpValue PHP native value type diff --git a/system/DataConverter/Cast/DatetimeCast.php b/system/DataCaster/Cast/DatetimeCast.php similarity index 96% rename from system/DataConverter/Cast/DatetimeCast.php rename to system/DataCaster/Cast/DatetimeCast.php index b2d06c5cb299..25ef20e1c881 100644 --- a/system/DataConverter/Cast/DatetimeCast.php +++ b/system/DataCaster/Cast/DatetimeCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; use CodeIgniter\I18n\Time; diff --git a/system/DataConverter/Cast/FloatCast.php b/system/DataCaster/Cast/FloatCast.php similarity index 94% rename from system/DataConverter/Cast/FloatCast.php rename to system/DataCaster/Cast/FloatCast.php index 86c500de9dfc..c8607b59be43 100644 --- a/system/DataConverter/Cast/FloatCast.php +++ b/system/DataCaster/Cast/FloatCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * Class FloatCast diff --git a/system/DataConverter/Cast/IntBoolCast.php b/system/DataCaster/Cast/IntBoolCast.php similarity index 95% rename from system/DataConverter/Cast/IntBoolCast.php rename to system/DataCaster/Cast/IntBoolCast.php index e5badd00fc98..b35e2261c468 100644 --- a/system/DataConverter/Cast/IntBoolCast.php +++ b/system/DataCaster/Cast/IntBoolCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * Int Bool Cast diff --git a/system/DataConverter/Cast/IntegerCast.php b/system/DataCaster/Cast/IntegerCast.php similarity index 94% rename from system/DataConverter/Cast/IntegerCast.php rename to system/DataCaster/Cast/IntegerCast.php index f0cad1ce0ef7..43049bf32fbc 100644 --- a/system/DataConverter/Cast/IntegerCast.php +++ b/system/DataCaster/Cast/IntegerCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; /** * Class IntegerCast diff --git a/system/DataConverter/Cast/JsonCast.php b/system/DataCaster/Cast/JsonCast.php similarity index 93% rename from system/DataConverter/Cast/JsonCast.php rename to system/DataCaster/Cast/JsonCast.php index fdedff4c3820..69554d1b0b9d 100644 --- a/system/DataConverter/Cast/JsonCast.php +++ b/system/DataCaster/Cast/JsonCast.php @@ -11,9 +11,9 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; -use CodeIgniter\DataConverter\Exceptions\CastException; +use CodeIgniter\DataCaster\Exceptions\CastException; use JsonException; use stdClass; diff --git a/system/DataConverter/Cast/TimestampCast.php b/system/DataCaster/Cast/TimestampCast.php similarity index 96% rename from system/DataConverter/Cast/TimestampCast.php rename to system/DataCaster/Cast/TimestampCast.php index 7d480e891a36..16bff6fdee33 100644 --- a/system/DataConverter/Cast/TimestampCast.php +++ b/system/DataCaster/Cast/TimestampCast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; use CodeIgniter\I18n\Time; diff --git a/system/DataConverter/Cast/URICast.php b/system/DataCaster/Cast/URICast.php similarity index 95% rename from system/DataConverter/Cast/URICast.php rename to system/DataCaster/Cast/URICast.php index 4f6a5284b971..da16a5cb9a19 100644 --- a/system/DataConverter/Cast/URICast.php +++ b/system/DataCaster/Cast/URICast.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Cast; +namespace CodeIgniter\DataCaster\Cast; use CodeIgniter\HTTP\URI; diff --git a/system/DataConverter/Exceptions/CastException.php b/system/DataCaster/Exceptions/CastException.php similarity index 91% rename from system/DataConverter/Exceptions/CastException.php rename to system/DataCaster/Exceptions/CastException.php index 51d72e5afed6..ff328b3deda1 100644 --- a/system/DataConverter/Exceptions/CastException.php +++ b/system/DataCaster/Exceptions/CastException.php @@ -11,7 +11,7 @@ * the LICENSE file that was distributed with this source code. */ -namespace CodeIgniter\DataConverter\Exceptions; +namespace CodeIgniter\DataCaster\Exceptions; use CodeIgniter\Entity\Exceptions\CastException as EntityCastException; diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index 9ee50ad94753..bcf640c1cb03 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -13,17 +13,17 @@ namespace CodeIgniter\DataConverter; -use CodeIgniter\DataConverter\Cast\ArrayCast; -use CodeIgniter\DataConverter\Cast\BooleanCast; -use CodeIgniter\DataConverter\Cast\CastInterface; -use CodeIgniter\DataConverter\Cast\CSVCast; -use CodeIgniter\DataConverter\Cast\DatetimeCast; -use CodeIgniter\DataConverter\Cast\FloatCast; -use CodeIgniter\DataConverter\Cast\IntBoolCast; -use CodeIgniter\DataConverter\Cast\IntegerCast; -use CodeIgniter\DataConverter\Cast\JsonCast; -use CodeIgniter\DataConverter\Cast\TimestampCast; -use CodeIgniter\DataConverter\Cast\URICast; +use CodeIgniter\DataCaster\Cast\ArrayCast; +use CodeIgniter\DataCaster\Cast\BooleanCast; +use CodeIgniter\DataCaster\Cast\CastInterface; +use CodeIgniter\DataCaster\Cast\CSVCast; +use CodeIgniter\DataCaster\Cast\DatetimeCast; +use CodeIgniter\DataCaster\Cast\FloatCast; +use CodeIgniter\DataCaster\Cast\IntBoolCast; +use CodeIgniter\DataCaster\Cast\IntegerCast; +use CodeIgniter\DataCaster\Cast\JsonCast; +use CodeIgniter\DataCaster\Cast\TimestampCast; +use CodeIgniter\DataCaster\Cast\URICast; use InvalidArgumentException; use TypeError; diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index 18d258f7c8d3..3c9f55c50c8e 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -472,7 +472,7 @@ public function testInvalidValue(): void { $this->expectException(TypeError::class); $this->expectExceptionMessage( - '[CodeIgniter\DataConverter\Cast\JsonCast] Invalid value type: bool, and its value: true' + '[CodeIgniter\DataCaster\Cast\JsonCast] Invalid value type: bool, and its value: true' ); $types = [ From e98c902e8247e5e1cf1b2e3fa27ef3cd094f026d Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:06:09 +0900 Subject: [PATCH 28/38] feat: add DataCaster class --- system/DataCaster/DataCaster.php | 189 +++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 system/DataCaster/DataCaster.php diff --git a/system/DataCaster/DataCaster.php b/system/DataCaster/DataCaster.php new file mode 100644 index 000000000000..f13ddb9809ce --- /dev/null +++ b/system/DataCaster/DataCaster.php @@ -0,0 +1,189 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\DataCaster; + +use CodeIgniter\DataCaster\Cast\ArrayCast; +use CodeIgniter\DataCaster\Cast\BooleanCast; +use CodeIgniter\DataCaster\Cast\CastInterface; +use CodeIgniter\DataCaster\Cast\CSVCast; +use CodeIgniter\DataCaster\Cast\DatetimeCast; +use CodeIgniter\DataCaster\Cast\FloatCast; +use CodeIgniter\DataCaster\Cast\IntBoolCast; +use CodeIgniter\DataCaster\Cast\IntegerCast; +use CodeIgniter\DataCaster\Cast\JsonCast; +use CodeIgniter\DataCaster\Cast\TimestampCast; +use CodeIgniter\DataCaster\Cast\URICast; +use CodeIgniter\Entity\Cast\CastInterface as EntityCastInterface; +use CodeIgniter\Entity\Exceptions\CastException; +use InvalidArgumentException; +use TypeError; + +final class DataCaster +{ + /** + * Array of field names and the type of value to cast. + * + * @var array [field => type] + */ + private array $types = []; + + /** + * Convert handlers + * + * @var array [type => classname] + */ + private array $castHandlers = [ + 'array' => ArrayCast::class, + 'bool' => BooleanCast::class, + 'boolean' => BooleanCast::class, + 'csv' => CSVCast::class, + 'datetime' => DatetimeCast::class, + 'double' => FloatCast::class, + 'float' => FloatCast::class, + 'int' => IntegerCast::class, + 'integer' => IntegerCast::class, + 'int-bool' => IntBoolCast::class, + 'json' => JsonCast::class, + 'timestamp' => TimestampCast::class, + 'uri' => URICast::class, + ]; + + /** + * Strict mode? Set to false for casts for Entity. + */ + private bool $strict; + + /** + * @param array|null $castHandlers Custom convert handlers + * @param array|null $types [field => type] + */ + public function __construct( + ?array $castHandlers = null, + ?array $types = null, + bool $strict = true + ) { + $this->castHandlers = array_merge($this->castHandlers, $castHandlers); + + if ($types !== null) { + $this->setTypes($types); + } + + $this->strict = $strict; + + if ($this->strict) { + foreach ($this->castHandlers as $handler) { + if ( + ! is_subclass_of($handler, CastInterface::class) + && ! is_subclass_of($handler, EntityCastInterface::class) + ) { + throw new InvalidArgumentException( + 'Invalid class type. It must implement CastInterface. class: ' . $handler + ); + } + } + } + } + + /** + * This method is only for Entity. + * + * @param array $types [field => type] + * + * $return $this + */ + public function setTypes(array $types): static + { + $this->types = $types; + + return $this; + } + + /** + * Provides the ability to cast an item as a specific data type. + * Add ? at the beginning of $type (i.e. ?string) to get `null` + * instead of casting $value if ($value === null). + * + * @param mixed $value The value to convert + * @param string $field The field name + * @param string $method Allowed to "get" and "set" + * @phpstan-param 'get'|'set' $method + */ + public function castAs(mixed $value, string $field, string $method = 'get'): mixed + { + // If the type is not defined, return as it is. + if (! isset($this->types[$field])) { + return $value; + } + + $type = $this->types[$field]; + + $isNullable = false; + + // Is nullable? + if (str_starts_with($type, '?')) { + $isNullable = true; + + if ($value === null) { + return null; + } + + $type = substr($type, 1); + } elseif ($value === null) { + if ($this->strict) { + $message = 'Field "' . $field . '" is not nullable, but null was passed.'; + + throw new TypeError($message); + } + } + + // In order not to create a separate handler for the + // json-array type, we transform the required one. + $type = ($type === 'json-array') ? 'json[array]' : $type; + + $params = []; + + // Attempt to retrieve additional parameters if specified + // type[param, param2,param3] + if (preg_match('/\A(.+)\[(.+)\]\z/', $type, $matches)) { + $type = $matches[1]; + $params = array_map('trim', explode(',', $matches[2])); + } + + if ($isNullable) { + $params[] = 'nullable'; + } + + $type = trim($type, '[]'); + + $handlers = $this->castHandlers; + + if (! isset($handlers[$type])) { + throw new InvalidArgumentException( + 'No such handler for "' . $field . '". Invalid type: ' . $type + ); + } + + $handler = $handlers[$type]; + + if ( + ! $this->strict + && ! is_subclass_of($handler, CastInterface::class) + && ! is_subclass_of($handler, EntityCastInterface::class) + ) { + throw CastException::forInvalidInterface($handler); + } + + return $handler::$method($value, $params); + } +} From ac9c5367cfa85da1cef3bea34c31f5f2320c01e2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:06:35 +0900 Subject: [PATCH 29/38] refactor: use DataCaster in DataConverter --- system/DataConverter/DataConverter.php | 135 ++----------------------- 1 file changed, 11 insertions(+), 124 deletions(-) diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index bcf640c1cb03..5bd2f3e40d0a 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -13,68 +13,27 @@ namespace CodeIgniter\DataConverter; -use CodeIgniter\DataCaster\Cast\ArrayCast; -use CodeIgniter\DataCaster\Cast\BooleanCast; -use CodeIgniter\DataCaster\Cast\CastInterface; -use CodeIgniter\DataCaster\Cast\CSVCast; -use CodeIgniter\DataCaster\Cast\DatetimeCast; -use CodeIgniter\DataCaster\Cast\FloatCast; -use CodeIgniter\DataCaster\Cast\IntBoolCast; -use CodeIgniter\DataCaster\Cast\IntegerCast; -use CodeIgniter\DataCaster\Cast\JsonCast; -use CodeIgniter\DataCaster\Cast\TimestampCast; -use CodeIgniter\DataCaster\Cast\URICast; -use InvalidArgumentException; -use TypeError; +use CodeIgniter\DataCaster\DataCaster; /** * PHP data <==> DataSource data converter * * @see \CodeIgniter\DataConverter\DataConverterTest */ -class DataConverter +final class DataConverter { /** - * Custom convert handlers - * - * @var array> [type => classname] - */ - protected $castHandlers = []; - - /** - * Default convert handlers - * - * @var array> [type => classname] + * The data caster. */ - private array $defaultCastHandlers = [ - 'array' => ArrayCast::class, - 'bool' => BooleanCast::class, - 'boolean' => BooleanCast::class, - 'csv' => CSVCast::class, - 'datetime' => DatetimeCast::class, - 'double' => FloatCast::class, - 'float' => FloatCast::class, - 'int' => IntegerCast::class, - 'integer' => IntegerCast::class, - 'int-bool' => IntBoolCast::class, - 'json' => JsonCast::class, - 'timestamp' => TimestampCast::class, - 'uri' => URICast::class, - ]; + private DataCaster $dataCaster; /** - * @param array $castHandlers Custom convert handlers + * @param array $types [column => type] + * @param array $castHandlers Custom convert handlers */ - public function __construct( - /** - * @var array [column => type] - */ - private array $types, - array $castHandlers = [] - ) { - if ($castHandlers !== []) { - $this->castHandlers = $castHandlers; - } + public function __construct(array $types, array $castHandlers = []) + { + $this->dataCaster = new DataCaster($castHandlers, $types); } /** @@ -87,7 +46,7 @@ public function fromDataSource(array $data): array $output = []; foreach ($data as $field => $value) { - $output[$field] = $this->castAs($value, $field, 'get'); + $output[$field] = $this->dataCaster->castAs($value, $field, 'get'); } return $output; @@ -103,81 +62,9 @@ public function toDataSource(array $phpData): array $output = []; foreach ($phpData as $field => $value) { - $output[$field] = $this->castAs($value, $field, 'set'); + $output[$field] = $this->dataCaster->castAs($value, $field, 'set'); } return $output; } - - /** - * Provides the ability to cast an item as a specific data type. - * Add ? at the beginning of $type (i.e. ?string) to get `null` - * instead of casting $value if ($value === null). - * - * @param mixed $value The value to convert - * @param string $field The field name - * @param string $method Allowed to "get" and "set" - * @phpstan-param 'get'|'set' $method - */ - protected function castAs($value, string $field, string $method = 'get'): mixed - { - // If the type is not defined, return as it is. - if (! isset($this->types[$field])) { - return $value; - } - - $type = $this->types[$field]; - - $isNullable = false; - - // Is nullable? - if (str_starts_with($type, '?')) { - $isNullable = true; - - if ($value === null) { - return null; - } - - $type = substr($type, 1); - } elseif ($value === null) { - $message = 'Field "' . $field . '" is not nullable, but null was passed.'; - - throw new TypeError($message); - } - - // In order not to create a separate handler for the - // json-array type, we transform the required one. - $type = ($type === 'json-array') ? 'json[array]' : $type; - - $params = []; - - // Attempt to retrieve additional parameters if specified - // type[param, param2,param3] - if (preg_match('/\A(.+)\[(.+)\]\z/', $type, $matches)) { - $type = $matches[1]; - $params = array_map('trim', explode(',', $matches[2])); - } - - if ($isNullable) { - $params[] = 'nullable'; - } - - $type = trim($type, '[]'); - - $handlers = array_merge($this->defaultCastHandlers, $this->castHandlers); - - if (! isset($handlers[$type])) { - throw new InvalidArgumentException('No such handler for "' . $field . '". Invalid type: ' . $type); - } - - $handler = $handlers[$type]; - - if (! is_subclass_of($handler, CastInterface::class)) { - throw new InvalidArgumentException( - 'Invalid class type. It must implement CastInterface. class: ' . $handler - ); - } - - return $handler::$method($value, $params); - } } From 31d8d799264d793ec3f1e7a70c29b8487da6faab Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:07:02 +0900 Subject: [PATCH 30/38] refactor: use DataCaster in Entity --- system/Entity/Entity.php | 68 +++++++++------------------------------- 1 file changed, 15 insertions(+), 53 deletions(-) diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php index ea8cd614c302..61e2b0213ea1 100644 --- a/system/Entity/Entity.php +++ b/system/Entity/Entity.php @@ -13,9 +13,9 @@ namespace CodeIgniter\Entity; +use CodeIgniter\DataCaster\DataCaster; use CodeIgniter\Entity\Cast\ArrayCast; use CodeIgniter\Entity\Cast\BooleanCast; -use CodeIgniter\Entity\Cast\CastInterface; use CodeIgniter\Entity\Cast\CSVCast; use CodeIgniter\Entity\Cast\DatetimeCast; use CodeIgniter\Entity\Cast\FloatCast; @@ -119,6 +119,11 @@ class Entity implements JsonSerializable */ protected $original = []; + /** + * The data caster. + */ + protected DataCaster $dataCaster; + /** * Holds info whenever properties have to be casted */ @@ -129,6 +134,12 @@ class Entity implements JsonSerializable */ public function __construct(?array $data = null) { + $this->dataCaster = new DataCaster( + array_merge($this->defaultCastHandlers, $this->castHandlers), + null, + false + ); + $this->syncOriginal(); $this->fill($data); @@ -362,58 +373,9 @@ protected function mutateDate($value) */ protected function castAs($value, string $attribute, string $method = 'get') { - if (empty($this->casts[$attribute])) { - return $value; - } - - $type = $this->casts[$attribute]; - - $isNullable = false; - - if (strpos($type, '?') === 0) { - $isNullable = true; - - if ($value === null) { - return null; - } - - $type = substr($type, 1); - } - - // In order not to create a separate handler for the - // json-array type, we transform the required one. - $type = $type === 'json-array' ? 'json[array]' : $type; - - if (! in_array($method, ['get', 'set'], true)) { - throw CastException::forInvalidMethod($method); - } - - $params = []; - - // Attempt to retrieve additional parameters if specified - // type[param, param2,param3] - if (preg_match('/^(.+)\[(.+)\]$/', $type, $matches)) { - $type = $matches[1]; - $params = array_map('trim', explode(',', $matches[2])); - } - - if ($isNullable) { - $params[] = 'nullable'; - } - - $type = trim($type, '[]'); - - $handlers = array_merge($this->defaultCastHandlers, $this->castHandlers); - - if (empty($handlers[$type])) { - return $value; - } - - if (! is_subclass_of($handlers[$type], CastInterface::class)) { - throw CastException::forInvalidInterface($handlers[$type]); - } - - return $handlers[$type]::$method($value, $params); + return $this->dataCaster + ->setTypes($this->casts) + ->castAs($value, $attribute, $method); } /** From dd4460328a2d9d020a175878958f5caeb0772487 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:34:04 +0900 Subject: [PATCH 31/38] chore: update phpstan-baseline --- phpstan-baseline.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index aa41b1ca6612..2be65f924fc0 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -1318,7 +1318,7 @@ ]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', - 'count' => 3, + 'count' => 1, 'path' => __DIR__ . '/system/Entity/Entity.php', ]; $ignoreErrors[] = [ From 868402d68946501c482616cdfdbedfa4f9e379cf Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:34:30 +0900 Subject: [PATCH 32/38] chore: update deptrac.yaml --- deptrac.yaml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/deptrac.yaml b/deptrac.yaml index 8aea2c5dd38e..349dfe16eb6c 100644 --- a/deptrac.yaml +++ b/deptrac.yaml @@ -35,6 +35,10 @@ parameters: collectors: - type: className regex: ^Codeigniter\\Database\\.* + - name: DataCaster + collectors: + - type: className + regex: ^Codeigniter\\DataCaster\\.* - name: DataConverter collectors: - type: className @@ -170,13 +174,16 @@ parameters: - Entity - Events - I18n - DataConverter: + DataCaster: - I18n - URI + DataConverter: + - DataCaster Email: - I18n - Events Entity: + - DataCaster - I18n Files: - I18n @@ -235,7 +242,10 @@ parameters: - CodeIgniter\HTTP\Header - CodeIgniter\HTTP\IncomingRequest - CodeIgniter\HTTP\ResponseInterface - CodeIgniter\DataConverter\Exceptions\CastException: + CodeIgniter\DataCaster\DataCaster: + - CodeIgniter\Entity\Cast\CastInterface + - CodeIgniter\Entity\Exceptions\CastException + CodeIgniter\DataCaster\Exceptions\CastException: - CodeIgniter\Entity\Exceptions\CastException CodeIgniter\Entity\Cast\URICast: - CodeIgniter\HTTP\URI From 07e8b635d5cfa4109423bc9d9daee0a874aafcef Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:34:44 +0900 Subject: [PATCH 33/38] chore: update psalm-baseline --- psalm-baseline.xml | 50 +--------------------------------------------- 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 4ced4dd7a851..1990e2a5901d 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + ]]> @@ -80,52 +80,11 @@ ];]]> - - - CastInterface - - - - - $value - $value - - - - - TDbColumn - TPhpValue - - - - - $value - - - - - $value - - - - - $value - - db->transStatus]]> - - - OCI_COMMIT_ON_SUCCESS - OCI_COMMIT_ON_SUCCESS - OCI_COMMIT_ON_SUCCESS - OCI_NO_AUTO_COMMIT - SQLT_CHR - - renderTimeline @@ -197,13 +156,6 @@ - - - OCI_ASSOC - OCI_B_CURSOR - OCI_RETURN_NULLS - - $current[$key] From 590f580ca7ede138b3c120e14d61668e5dcb69d8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 16:37:11 +0900 Subject: [PATCH 34/38] test: add missing parent::__construct() --- tests/system/View/ParserTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/system/View/ParserTest.php b/tests/system/View/ParserTest.php index 9da61684516e..e724edc1d4eb 100644 --- a/tests/system/View/ParserTest.php +++ b/tests/system/View/ParserTest.php @@ -377,6 +377,8 @@ public function testParseLoopEntityObjectProperties(): void public function __construct() { + parent::__construct(); + $this->obj1 = new stdClass(); $this->obj2 = new stdClass(); $this->obj1->name = 'first'; From 942eddbc40f430fe753954dfde83e279942f286d Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 19:06:23 +0900 Subject: [PATCH 35/38] docs: remove PHPDoc @template Psalm does not support class @template in static methods. And in PHPStan it does not protect. See - https://github.com/vimeo/psalm/issues/2697 - https://github.com/phpstan/phpstan/issues/2738 --- system/DataCaster/Cast/ArrayCast.php | 2 -- system/DataCaster/Cast/BaseCast.php | 7 ------- system/DataCaster/Cast/BooleanCast.php | 2 -- system/DataCaster/Cast/CSVCast.php | 2 -- system/DataCaster/Cast/CastInterface.php | 17 ++++++----------- system/DataCaster/Cast/DatetimeCast.php | 2 -- system/DataCaster/Cast/FloatCast.php | 2 -- system/DataCaster/Cast/IntBoolCast.php | 2 -- system/DataCaster/Cast/IntegerCast.php | 2 -- system/DataCaster/Cast/JsonCast.php | 2 -- system/DataCaster/Cast/TimestampCast.php | 2 -- system/DataCaster/Cast/URICast.php | 2 -- 12 files changed, 6 insertions(+), 38 deletions(-) diff --git a/system/DataCaster/Cast/ArrayCast.php b/system/DataCaster/Cast/ArrayCast.php index c13d73231ddf..8aa044f4fe71 100644 --- a/system/DataCaster/Cast/ArrayCast.php +++ b/system/DataCaster/Cast/ArrayCast.php @@ -18,8 +18,6 @@ * * (PHP) [array --> string] --> (DB driver) --> (DB column) string * [ <-- string] <-- (DB driver) <-- (DB column) string - * - * @extends BaseCast */ class ArrayCast extends BaseCast implements CastInterface { diff --git a/system/DataCaster/Cast/BaseCast.php b/system/DataCaster/Cast/BaseCast.php index 3b4a5403d7da..e33a22d178f6 100644 --- a/system/DataCaster/Cast/BaseCast.php +++ b/system/DataCaster/Cast/BaseCast.php @@ -15,13 +15,6 @@ use TypeError; -/** - * @template TPhpValue PHP data type - * @template TToDb Data type to pass to database driver - * @template TDbColumn Data type from database driver - * - * @implements CastInterface - */ abstract class BaseCast implements CastInterface { public static function get(mixed $value, array $params = []): mixed diff --git a/system/DataCaster/Cast/BooleanCast.php b/system/DataCaster/Cast/BooleanCast.php index 07ebea2041ed..646dd447f006 100644 --- a/system/DataCaster/Cast/BooleanCast.php +++ b/system/DataCaster/Cast/BooleanCast.php @@ -18,8 +18,6 @@ * * (PHP) [bool --> bool ] --> (DB driver) --> (DB column) bool|int(0/1) * [ <-- string|int] <-- (DB driver) <-- (DB column) bool|int(0/1) - * - * @extends BaseCast */ class BooleanCast extends BaseCast { diff --git a/system/DataCaster/Cast/CSVCast.php b/system/DataCaster/Cast/CSVCast.php index a834068a9512..89e24259073a 100644 --- a/system/DataCaster/Cast/CSVCast.php +++ b/system/DataCaster/Cast/CSVCast.php @@ -18,8 +18,6 @@ * * (PHP) [array --> string] --> (DB driver) --> (DB column) string * [ <-- string] <-- (DB driver) <-- (DB column) string - * - * @extends BaseCast */ class CSVCast extends BaseCast { diff --git a/system/DataCaster/Cast/CastInterface.php b/system/DataCaster/Cast/CastInterface.php index f8a11b0c2376..eef4983ce942 100644 --- a/system/DataCaster/Cast/CastInterface.php +++ b/system/DataCaster/Cast/CastInterface.php @@ -13,30 +13,25 @@ namespace CodeIgniter\DataCaster\Cast; -/** - * @template TPhpValue PHP native value type - * @template TToDb Data type to pass to database driver - * @template TDbColumn Data type from database driver - */ interface CastInterface { /** - * Takes value from DataSource, returns its value for PHP. + * Takes a value from DataSource, returns its value for PHP. * - * @param TDbColumn $value Data from database driver + * @param mixed $value Data from database driver * @param list $params Additional param * - * @return TPhpValue PHP native value + * @return mixed PHP native value */ public static function get(mixed $value, array $params = []): mixed; /** - * Takes the PHP value, returns its value for DataSource. + * Takes a PHP value, returns its value for DataSource. * - * @param TPhpValue $value PHP native value + * @param mixed $value PHP native value * @param list $params Additional param * - * @return TToDb Data to pass to database driver + * @return mixed Data to pass to database driver */ public static function set(mixed $value, array $params = []): mixed; } diff --git a/system/DataCaster/Cast/DatetimeCast.php b/system/DataCaster/Cast/DatetimeCast.php index 25ef20e1c881..3995964198c6 100644 --- a/system/DataCaster/Cast/DatetimeCast.php +++ b/system/DataCaster/Cast/DatetimeCast.php @@ -20,8 +20,6 @@ * * (PHP) [Time --> string] --> (DB driver) --> (DB column) datetime * [ <-- string] <-- (DB driver) <-- (DB column) datetime - * - * @extends BaseCast */ class DatetimeCast extends BaseCast { diff --git a/system/DataCaster/Cast/FloatCast.php b/system/DataCaster/Cast/FloatCast.php index c8607b59be43..c2c6b943adce 100644 --- a/system/DataCaster/Cast/FloatCast.php +++ b/system/DataCaster/Cast/FloatCast.php @@ -18,8 +18,6 @@ * * (PHP) [float --> float ] --> (DB driver) --> (DB column) float * [ <-- float|string] <-- (DB driver) <-- (DB column) float - * - * @extends BaseCast */ class FloatCast extends BaseCast { diff --git a/system/DataCaster/Cast/IntBoolCast.php b/system/DataCaster/Cast/IntBoolCast.php index b35e2261c468..70c2aaa57674 100644 --- a/system/DataCaster/Cast/IntBoolCast.php +++ b/system/DataCaster/Cast/IntBoolCast.php @@ -18,8 +18,6 @@ * * (PHP) [bool --> int ] --> (DB driver) --> (DB column) int(0/1) * [ <-- int|string] <-- (DB driver) <-- (DB column) int(0/1) - * - * @extends BaseCast */ final class IntBoolCast extends BaseCast { diff --git a/system/DataCaster/Cast/IntegerCast.php b/system/DataCaster/Cast/IntegerCast.php index 43049bf32fbc..158f61b9dfbf 100644 --- a/system/DataCaster/Cast/IntegerCast.php +++ b/system/DataCaster/Cast/IntegerCast.php @@ -18,8 +18,6 @@ * * (PHP) [int --> int ] --> (DB driver) --> (DB column) int * [ <-- int|string] <-- (DB driver) <-- (DB column) int - * - * @extends BaseCast */ class IntegerCast extends BaseCast { diff --git a/system/DataCaster/Cast/JsonCast.php b/system/DataCaster/Cast/JsonCast.php index 69554d1b0b9d..b25a4b5966aa 100644 --- a/system/DataCaster/Cast/JsonCast.php +++ b/system/DataCaster/Cast/JsonCast.php @@ -22,8 +22,6 @@ * * (PHP) [array|stdClass --> string] --> (DB driver) --> (DB column) string * [ <-- string] <-- (DB driver) <-- (DB column) string - * - * @extends BaseCast */ class JsonCast extends BaseCast { diff --git a/system/DataCaster/Cast/TimestampCast.php b/system/DataCaster/Cast/TimestampCast.php index 16bff6fdee33..0a871a28cbc9 100644 --- a/system/DataCaster/Cast/TimestampCast.php +++ b/system/DataCaster/Cast/TimestampCast.php @@ -20,8 +20,6 @@ * * (PHP) [Time --> int ] --> (DB driver) --> (DB column) int * [ <-- int|string] <-- (DB driver) <-- (DB column) int - * - * @extends BaseCast */ class TimestampCast extends BaseCast { diff --git a/system/DataCaster/Cast/URICast.php b/system/DataCaster/Cast/URICast.php index da16a5cb9a19..cf9b58df6eac 100644 --- a/system/DataCaster/Cast/URICast.php +++ b/system/DataCaster/Cast/URICast.php @@ -20,8 +20,6 @@ * * (PHP) [URI --> string] --> (DB driver) --> (DB column) string * [ <-- string] <-- (DB driver) <-- (DB column) string - * - * @extends BaseCast */ class URICast extends BaseCast { From 567372e452148aed177b7aff4412b4b84988694e Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 23 Jan 2024 21:54:10 +0900 Subject: [PATCH 36/38] docs: make @param more strict --- system/DataConverter/DataConverter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/DataConverter/DataConverter.php b/system/DataConverter/DataConverter.php index 5bd2f3e40d0a..09088bd34b0b 100644 --- a/system/DataConverter/DataConverter.php +++ b/system/DataConverter/DataConverter.php @@ -28,8 +28,8 @@ final class DataConverter private DataCaster $dataCaster; /** - * @param array $types [column => type] - * @param array $castHandlers Custom convert handlers + * @param array $types [column => type] + * @param array $castHandlers Custom convert handlers */ public function __construct(array $types, array $castHandlers = []) { From 029d1186b78080a44578fc598651a19ba1bd0933 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jan 2024 11:35:05 +0900 Subject: [PATCH 37/38] docs: fix doc comments in Entity/Cast/CastInterface --- system/Entity/Cast/CastInterface.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/system/Entity/Cast/CastInterface.php b/system/Entity/Cast/CastInterface.php index 0a17ec0861e0..9d790e8edbb9 100644 --- a/system/Entity/Cast/CastInterface.php +++ b/system/Entity/Cast/CastInterface.php @@ -15,11 +15,15 @@ /** * Interface CastInterface + * + * The methods work at (1)(4) only. + * [App Code] --- (1) --> [Entity] --- (2) --> [Database] + * [App Code] <-- (4) --- [Entity] <-- (3) --- [Database] */ interface CastInterface { /** - * Takes a value from Database, returns its value for PHP. + * Takes a raw value from Entity, returns its value for PHP. * * @param array|bool|float|int|object|string|null $value Data * @param array $params Additional param @@ -29,7 +33,7 @@ interface CastInterface public static function get($value, array $params = []); /** - * Takes a PHP value, returns its value for Database. + * Takes a PHP value, returns its raw value for Entity. * * @param array|bool|float|int|object|string|null $value Data * @param array $params Additional param From 7199ac41f481044f8311f25be18d3bd5ec662bc8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jan 2024 11:35:31 +0900 Subject: [PATCH 38/38] docs: update @param style --- system/DataCaster/DataCaster.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/DataCaster/DataCaster.php b/system/DataCaster/DataCaster.php index f13ddb9809ce..d68dd6babdc4 100644 --- a/system/DataCaster/DataCaster.php +++ b/system/DataCaster/DataCaster.php @@ -114,9 +114,9 @@ public function setTypes(array $types): static * Add ? at the beginning of $type (i.e. ?string) to get `null` * instead of casting $value if ($value === null). * - * @param mixed $value The value to convert - * @param string $field The field name - * @param string $method Allowed to "get" and "set" + * @param mixed $value The value to convert + * @param string $field The field name + * @param string $method Allowed to "get" and "set" * @phpstan-param 'get'|'set' $method */ public function castAs(mixed $value, string $field, string $method = 'get'): mixed