Skip to content

Commit 6e0a636

Browse files
committed
[fix] Cacher/EnumDefaultRequiredValueException
1 parent e1bcfa3 commit 6e0a636

5 files changed

Lines changed: 83 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# CHANGELOG
22

3+
## [v4.5.1] - 2026-05-29
4+
5+
### Fixed
6+
7+
- **`ib-cacher` discarded Enum case default values, causing `RequiredValueException` at runtime.**
8+
`Cacher::containsNonSerializable()` treated any object as non-serializable via a bare `is_object()` check, including Enum cases. Defaults declared in `defaultValues()` as Enum cases were silently replaced with `null` in the generated cache. At runtime there is no fallback to re-invoke `defaultValues()`, so any property with `allowsNull = false` and an Enum default would throw `RequiredValueException` whenever a cache file was present — even though the same call succeeded without a cache.
9+
10+
Fixed by checking `instanceof \UnitEnum` before `is_object()`. PHP 8.1+ `var_export()` serializes all Enum cases (backed and pure) as `\ClassName::CaseName`, which is a valid PHP expression that reconstructs the correct case on `require`.
11+
312
## [v4.5.0] - 2026-05-28
413

514
### Added

src/CLI/Cacher.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ private static function defaultValueValidate(string $classname, array $defaults,
217217
*/
218218
private static function containsNonSerializable(mixed $value): bool
219219
{
220+
if ($value instanceof \UnitEnum) {
221+
return false;
222+
}
220223
if (\is_object($value)) {
221224
return true;
222225
}

tests/Bugs/MinimalDTO.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Tests\Bugs;
4+
5+
use ReallifeKip\ImmutableBase\Objects\DataTransferObject;
6+
7+
readonly class MinimalDTO extends DataTransferObject
8+
{
9+
public StatusEnum $status;
10+
11+
public static function defaultValues(): array
12+
{
13+
return [
14+
'status' => StatusEnum::Active,
15+
];
16+
}
17+
}

tests/Bugs/StatusEnum.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Tests\Bugs;
4+
5+
enum StatusEnum: string
6+
{
7+
case Active = 'active';
8+
}

tests/CLI/CacherTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,52 @@ public function testLoadCacheSkipsWhenFileNotExists(): void
236236
$this->assertEmpty(ImmutableBase::state()['cachedMeta']);
237237
}
238238

239+
public function testEnumCaseDefaultIsPreservedInCache(): void
240+
{
241+
$initialLevel = ob_get_level();
242+
try {
243+
ob_start();
244+
(new Cacher())->scan($this->scanDir);
245+
} finally {
246+
while (ob_get_level() > $initialLevel) {
247+
ob_end_clean();
248+
}
249+
}
250+
251+
$cache = require $this->cachePath;
252+
$statusDefault = $cache[\Tests\Bugs\MinimalDTO::class]['types']['status']['defaults'] ?? 'missing';
253+
254+
$this->assertInstanceOf(
255+
\Tests\Bugs\StatusEnum::class,
256+
$statusDefault,
257+
'Enum case default must be preserved in cache, not replaced with null'
258+
);
259+
$this->assertSame(\Tests\Bugs\StatusEnum::Active, $statusDefault);
260+
}
261+
262+
public function testEnumCaseDefaultWorksAfterLoadingCache(): void
263+
{
264+
$initialLevel = ob_get_level();
265+
try {
266+
ob_start();
267+
(new Cacher())->scan($this->scanDir);
268+
} finally {
269+
while (ob_get_level() > $initialLevel) {
270+
ob_end_clean();
271+
}
272+
}
273+
274+
$s = &ImmutableBase::state();
275+
$s['cachedMeta'] = [];
276+
$s['cachePath'] = null;
277+
$s['properties'] = [];
278+
$s['refs'] = [];
279+
ImmutableBase::loadCache();
280+
281+
$dto = \Tests\Bugs\MinimalDTO::fromArray([]);
282+
$this->assertSame(\Tests\Bugs\StatusEnum::Active, $dto->status);
283+
}
284+
239285
public function testCachedMetaRestoredDuringBuild(): void
240286
{
241287
$initialLevel = ob_get_level();

0 commit comments

Comments
 (0)