Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions system/DataCaster/Cast/CastInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ interface CastInterface
/**
* Takes a value from DataSource, returns its value for PHP.
*
* @param mixed $value Data from database driver
* @param list<string> $params Additional param
* @param object|null $helper Helper object. E.g., database connection
* @param mixed $value Data from database driver
* @param array<int, string> $params Additional param
* @param object|null $helper Helper object. E.g., database connection
*
* @return mixed PHP native value
*/
Expand All @@ -33,9 +33,9 @@ public static function get(
/**
* Takes a PHP value, returns its value for DataSource.
*
* @param mixed $value PHP native value
* @param list<string> $params Additional param
* @param object|null $helper Helper object. E.g., database connection
* @param mixed $value PHP native value
* @param array<int, string> $params Additional param
* @param object|null $helper Helper object. E.g., database connection
*
* @return mixed Data to pass to database driver
*/
Expand Down
120 changes: 120 additions & 0 deletions system/DataCaster/Cast/EnumCast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\DataCaster\Cast;

use BackedEnum;
use CodeIgniter\DataCaster\Exceptions\CastException;
use ReflectionEnum;
use UnitEnum;

/**
* Class EnumCast
*
* Handles casting for PHP enums (both backed and unit enums)
*
* (PHP) [enum --> value/name] --> (DB driver) --> (DB column) int|string
* [ <-- value/name] <-- (DB driver) <-- (DB column) int|string
*/
class EnumCast extends BaseCast implements CastInterface
{
public static function get(
mixed $value,
array $params = [],
?object $helper = null,
): mixed {
if (! is_string($value) && ! is_int($value)) {
self::invalidTypeValueError($value);
}

$enumClass = $params[0] ?? null;

if ($enumClass === null) {
throw CastException::forMissingEnumClass();
}

if (! enum_exists($enumClass)) {
throw CastException::forNotEnum($enumClass);
}

$reflection = new ReflectionEnum($enumClass);

// Unit enum
if (! $reflection->isBacked()) {
// Unit enum - match by name
foreach ($enumClass::cases() as $case) {
if ($case->name === $value) {
return $case;
}
}

throw CastException::forInvalidEnumCaseName($enumClass, $value);
}

// Backed enum - validate and cast the value to proper type
$backingType = $reflection->getBackingType();

// Cast to proper type (int or string)
if ($backingType->getName() === 'int') {
$value = (int) $value;
} elseif ($backingType->getName() === 'string') {
$value = (string) $value;
}

$enum = $enumClass::tryFrom($value);

if ($enum === null) {
throw CastException::forInvalidEnumValue($enumClass, $value);
}

return $enum;
}

public static function set(
mixed $value,
array $params = [],
?object $helper = null,
): int|string {
if (! is_object($value) || ! enum_exists($value::class)) {
self::invalidTypeValueError($value);
}

// Get the expected enum class
$enumClass = $params[0] ?? null;

if ($enumClass === null) {
throw CastException::forMissingEnumClass();
}

if (! enum_exists($enumClass)) {
throw CastException::forNotEnum($enumClass);
}

// Validate that the enum is of the expected type
if (! $value instanceof $enumClass) {
throw CastException::forInvalidEnumType($enumClass, $value::class);
}

$reflection = new ReflectionEnum($value::class);

// Backed enum - return the properly typed backing value
if ($reflection->isBacked()) {
/** @var BackedEnum $value */
return $value->value;
}

// Unit enum - return the case name
/** @var UnitEnum $value */
return $value->name;
}
}
2 changes: 2 additions & 0 deletions system/DataCaster/DataCaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use CodeIgniter\DataCaster\Cast\CastInterface;
use CodeIgniter\DataCaster\Cast\CSVCast;
use CodeIgniter\DataCaster\Cast\DatetimeCast;
use CodeIgniter\DataCaster\Cast\EnumCast;
use CodeIgniter\DataCaster\Cast\FloatCast;
use CodeIgniter\DataCaster\Cast\IntBoolCast;
use CodeIgniter\DataCaster\Cast\IntegerCast;
Expand Down Expand Up @@ -48,6 +49,7 @@ final class DataCaster
'boolean' => BooleanCast::class,
'csv' => CSVCast::class,
'datetime' => DatetimeCast::class,
'enum' => EnumCast::class,
'double' => FloatCast::class,
'float' => FloatCast::class,
'int' => IntegerCast::class,
Expand Down
16 changes: 0 additions & 16 deletions system/Entity/Cast/BaseCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,11 @@
*/
abstract class BaseCast implements CastInterface
{
/**
* Get
*
* @param array|bool|float|int|object|string|null $value Data
* @param array $params Additional param
*
* @return array|bool|float|int|object|string|null
*/
public static function get($value, array $params = [])
{
return $value;
}

/**
* Set
*
* @param array|bool|float|int|object|string|null $value Data
* @param array $params Additional param
*
* @return array|bool|float|int|object|string|null
*/
public static function set($value, array $params = [])
{
return $value;
Expand Down
4 changes: 2 additions & 2 deletions system/Entity/Cast/CastInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface CastInterface
* 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
* @param array<int, string> $params Additional param
*
* @return array|bool|float|int|object|string|null
*/
Expand All @@ -36,7 +36,7 @@ public static function get($value, array $params = []);
* 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
* @param array<int, string> $params Additional param
*
* @return array|bool|float|int|object|string|null
*/
Expand Down
131 changes: 131 additions & 0 deletions system/Entity/Cast/EnumCast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Entity\Cast;

use BackedEnum;
use CodeIgniter\Entity\Exceptions\CastException;
use ReflectionEnum;
use UnitEnum;

class EnumCast extends BaseCast
{
public static function get($value, array $params = [])
{
$enumClass = $params[0] ?? null;

if ($enumClass === null) {
throw CastException::forMissingEnumClass();
}

if (! enum_exists($enumClass)) {
throw CastException::forNotEnum($enumClass);
}

$reflection = new ReflectionEnum($enumClass);

// Backed enum - validate and cast the value to proper type
if ($reflection->isBacked()) {
$backingType = $reflection->getBackingType();

// Cast to proper type (int or string)
if ($backingType->getName() === 'int') {
$value = (int) $value;
} elseif ($backingType->getName() === 'string') {
$value = (string) $value;
}

$enum = $enumClass::tryFrom($value);

if ($enum === null) {
throw CastException::forInvalidEnumValue($enumClass, $value);
}

return $enum;
}

// Unit enum - match by name
foreach ($enumClass::cases() as $case) {
if ($case->name === $value) {
return $case;
}
}

throw CastException::forInvalidEnumCaseName($enumClass, $value);
}

public static function set($value, array $params = []): int|string
{
// Get the expected enum class
$enumClass = $params[0] ?? null;

if ($enumClass === null) {
throw CastException::forMissingEnumClass();
}

if (! enum_exists($enumClass)) {
throw CastException::forNotEnum($enumClass);
}

// If it's already an enum object, validate and extract its value
if (is_object($value) && enum_exists($value::class)) {
// Validate that the enum is of the expected type
if (! $value instanceof $enumClass) {
throw CastException::forInvalidEnumType($enumClass, $value::class);
}

$reflection = new ReflectionEnum($value::class);

// Backed enum - return the properly typed backing value
if ($reflection->isBacked()) {
/** @var BackedEnum $value */
return $value->value;
}

// Unit enum - return the case name
/** @var UnitEnum $value */
return $value->name;
}

$reflection = new ReflectionEnum($enumClass);

// Validate backed enum values
if ($reflection->isBacked()) {
$backingType = $reflection->getBackingType();

// Cast to proper type (int or string)
if ($backingType->getName() === 'int') {
$value = (int) $value;
} elseif ($backingType->getName() === 'string') {
$value = (string) $value;
}

if ($enumClass::tryFrom($value) === null) {
throw CastException::forInvalidEnumValue($enumClass, $value);
}

return $value;
}

// Validate unit enum case names - must be a string
$value = (string) $value;

foreach ($enumClass::cases() as $case) {
if ($case->name === $value) {
return $value;
}
}

throw CastException::forInvalidEnumCaseName($enumClass, $value);
}
}
2 changes: 2 additions & 0 deletions system/Entity/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use CodeIgniter\Entity\Cast\BooleanCast;
use CodeIgniter\Entity\Cast\CSVCast;
use CodeIgniter\Entity\Cast\DatetimeCast;
use CodeIgniter\Entity\Cast\EnumCast;
use CodeIgniter\Entity\Cast\FloatCast;
use CodeIgniter\Entity\Cast\IntBoolCast;
use CodeIgniter\Entity\Cast\IntegerCast;
Expand Down Expand Up @@ -92,6 +93,7 @@ class Entity implements JsonSerializable
'csv' => CSVCast::class,
'datetime' => DatetimeCast::class,
'double' => FloatCast::class,
'enum' => EnumCast::class,
'float' => FloatCast::class,
'int' => IntegerCast::class,
'integer' => IntegerCast::class,
Expand Down
Loading
Loading