Skip to content

Commit 14c7ebe

Browse files
innerflyinnerfly
andauthored
Prevent serialization of non-backed enums (#6796)
* #6753 Prevent serialization of non-backed enums * add normalization for json encoding * enum serialization test * add string and array serialization * add string and array serialization * prettify * fix indendation * enum processing optimization --------- Co-authored-by: innerfly <you@example.com>
1 parent 83c2986 commit 14c7ebe

2 files changed

Lines changed: 136 additions & 0 deletions

File tree

src/Codeception/Test/Descriptor.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
namespace Codeception\Test;
66

7+
use BackedEnum;
78
use Codeception\Test\Interfaces\Descriptive;
89
use Codeception\Test\Interfaces\Plain;
910
use Codeception\TestInterface;
1011
use PHPUnit\Framework\SelfDescribing;
12+
use UnitEnum;
1113

1214
use function codecept_relative_path;
1315
use function json_encode;
@@ -34,6 +36,7 @@ public static function getTestSignatureUnique(SelfDescribing $testCase): string
3436
$signature .= ':' . $env;
3537
}
3638
if (method_exists($testCase, 'getMetadata') && $example = $testCase->getMetadata()->getCurrent('example')) {
39+
$example = self::normalizeForJsonEncoding($example);
3740
$encoded = json_encode($example, JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE);
3841
$signature .= ':' . substr(sha1($encoded), 0, 7);
3942
}
@@ -85,4 +88,13 @@ public static function getTestDataSetIndex(SelfDescribing $testCase): string
8588
default => '',
8689
};
8790
}
91+
92+
private static function normalizeForJsonEncoding(mixed $value): mixed
93+
{
94+
return match (true) {
95+
is_array($value) => array_map(self::normalizeForJsonEncoding(...), $value),
96+
$value instanceof UnitEnum && !($value instanceof BackedEnum) => $value->name,
97+
default => $value,
98+
};
99+
}
88100
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace unit\Codeception\Test;
6+
7+
use Codeception\Test\Descriptor;
8+
use Codeception\Test\Interfaces\Descriptive;
9+
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
10+
11+
enum UnitEnumExample
12+
{
13+
case FOO;
14+
case BAR;
15+
}
16+
17+
enum BackedEnumExample: string
18+
{
19+
case FOO = 'one';
20+
case BAR = 'two';
21+
}
22+
23+
class TestCase implements Descriptive
24+
{
25+
public function toString(): string
26+
{
27+
return 'TestCase';
28+
}
29+
30+
public function getFileName(): string
31+
{
32+
return __FILE__;
33+
}
34+
35+
public function getSignature(): string
36+
{
37+
return 'TestCaseSignature';
38+
}
39+
}
40+
41+
class DescriptorTest extends PHPUnitTestCase
42+
{
43+
public function testUnitEnumSerialization(): void
44+
{
45+
$testCase = new class extends TestCase {
46+
public function getMetadata(): object
47+
{
48+
return new class {
49+
public function getCurrent(string $key): mixed
50+
{
51+
return ['enum' => UnitEnumExample::FOO];
52+
}
53+
};
54+
}
55+
};
56+
57+
$signature = Descriptor::getTestSignatureUnique($testCase);
58+
$this->assertSame($testCase->getSignature() . ':41e8901', $signature);
59+
}
60+
61+
public function testBackedEnumSerialization(): void
62+
{
63+
$testCase = new class extends TestCase {
64+
public function getMetadata(): object
65+
{
66+
return new class {
67+
public function getCurrent(string $key): mixed
68+
{
69+
return ['enum' => BackedEnumExample::FOO];
70+
}
71+
};
72+
}
73+
};
74+
75+
$signature = Descriptor::getTestSignatureUnique($testCase);
76+
$this->assertSame($testCase->getSignature() . ':f863384', $signature);
77+
}
78+
79+
public function testStringSerialization(): void
80+
{
81+
$testCase = new class extends TestCase {
82+
public function getMetadata(): object
83+
{
84+
return new class {
85+
public function getCurrent(string $key): mixed
86+
{
87+
return ['string' => 'test value'];
88+
}
89+
};
90+
}
91+
};
92+
93+
$signature = Descriptor::getTestSignatureUnique($testCase);
94+
$this->assertSame($testCase->getSignature() . ':5cf307a', $signature);
95+
}
96+
97+
public function testArraySerialization(): void
98+
{
99+
$testCase = new class extends TestCase {
100+
public function getMetadata(): object
101+
{
102+
return new class {
103+
public function getCurrent(string $key): mixed
104+
{
105+
return [
106+
'array' => [
107+
'one',
108+
'two',
109+
'three' => [
110+
'key' => 'value',
111+
'enum1' => UnitEnumExample::FOO,
112+
'enum2' => BackedEnumExample::BAR,
113+
]
114+
],
115+
];
116+
}
117+
};
118+
}
119+
};
120+
121+
$signature = Descriptor::getTestSignatureUnique($testCase);
122+
$this->assertSame($testCase->getSignature() . ':8aed566', $signature);
123+
}
124+
}

0 commit comments

Comments
 (0)