5
5
namespace ComplexHeart \Domain \Model \Traits ;
6
6
7
7
use ComplexHeart \Domain \Model \Exceptions \InvariantViolation ;
8
- use Exception ;
8
+ use Throwable ;
9
9
10
10
/**
11
11
* Trait HasInvariants
15
15
*/
16
16
trait HasInvariants
17
17
{
18
+ /**
19
+ * Static property to keep cached invariants list to optimize performance.
20
+ *
21
+ * @var array<string, <string, string>>
22
+ */
23
+ private static $ _invariantsCache = [];
24
+
18
25
/**
19
26
* Retrieve the object invariants.
20
27
*
21
28
* @return string[]
22
29
*/
23
30
final public static function invariants (): array
24
31
{
25
- $ invariants = [];
26
- foreach (get_class_methods (static ::class) as $ invariant ) {
27
- if (str_starts_with ($ invariant , 'invariant ' ) && !in_array ($ invariant , ['invariants ' , 'invariantHandler ' ])) {
28
- $ invariantRuleName = preg_replace ('/[A-Z]([A-Z](?![a-z]))*/ ' , ' $0 ' , $ invariant );
29
- if (is_null ($ invariantRuleName )) {
30
- continue ;
31
- }
32
+ if (array_key_exists (static ::class, static ::$ _invariantsCache ) === false ) {
33
+ $ invariants = [];
34
+ foreach (get_class_methods (static ::class) as $ invariant ) {
35
+ if (str_starts_with ($ invariant , 'invariant ' ) && !in_array ($ invariant ,
36
+ ['invariants ' , 'invariantHandler ' ])) {
37
+ $ invariantRuleName = preg_replace ('/[A-Z]([A-Z](?![a-z]))*/ ' , ' $0 ' , $ invariant );
38
+ if (is_null ($ invariantRuleName )) {
39
+ continue ;
40
+ }
32
41
33
- $ invariants [$ invariant ] = str_replace ('invariant ' , '' , strtolower ($ invariantRuleName ));
42
+ $ invariants [$ invariant ] = str_replace ('invariant ' , '' , strtolower ($ invariantRuleName ));
43
+ }
34
44
}
45
+
46
+ static ::$ _invariantsCache [static ::class] = $ invariants ;
35
47
}
36
48
37
- return $ invariants ;
49
+ return static :: $ _invariantsCache [ static ::class] ;
38
50
}
39
51
40
52
/**
@@ -52,53 +64,60 @@ final public static function invariants(): array
52
64
* If exception is thrown the error message will be the exception message.
53
65
*
54
66
* $onFail function must have the following signature:
55
- * fn(array<string, string >) => void
67
+ * fn(array<string, Throwable >) => void
56
68
*
57
69
* @param string|callable $onFail
70
+ * @param string $exception
58
71
*
59
72
* @return void
60
73
*/
61
- private function check (string |callable $ onFail = 'invariantHandler ' ): void
62
- {
63
- $ violations = $ this ->computeInvariantViolations ();
74
+ private function check (
75
+ string |callable $ onFail = 'invariantHandler ' ,
76
+ string $ exception = InvariantViolation::class
77
+ ): void {
78
+ $ violations = $ this ->computeInvariantViolations ($ exception );
64
79
if (!empty ($ violations )) {
65
- call_user_func_array ($ this ->computeInvariantHandler ($ onFail ), [$ violations ]);
80
+ call_user_func_array ($ this ->computeInvariantHandler ($ onFail, $ exception ), [$ violations ]);
66
81
}
67
82
}
68
83
69
84
/**
70
85
* Computes the list of invariant violations.
71
86
*
72
- * @return array<string, string >
87
+ * @return array<string, Throwable >
73
88
*/
74
- private function computeInvariantViolations (): array
89
+ private function computeInvariantViolations (string $ exception ): array
75
90
{
76
91
$ violations = [];
77
92
foreach (static ::invariants () as $ invariant => $ rule ) {
78
93
try {
79
94
if (!$ this ->{$ invariant }()) {
80
- $ violations [$ invariant ] = $ rule ;
95
+ $ violations [$ invariant ] = new $ exception ( $ rule) ;
81
96
}
82
- } catch (Exception $ e ) {
83
- $ violations [$ invariant ] = $ e-> getMessage () ;
97
+ } catch (Throwable $ e ) {
98
+ $ violations [$ invariant ] = $ e ;
84
99
}
85
100
}
86
101
87
102
return $ violations ;
88
103
}
89
104
90
- private function computeInvariantHandler (string |callable $ handlerFn ): callable
105
+ private function computeInvariantHandler (string |callable $ handlerFn, string $ exception ): callable
91
106
{
92
107
if (!is_string ($ handlerFn )) {
93
108
return $ handlerFn ;
94
109
}
95
110
96
111
return method_exists ($ this , $ handlerFn )
97
- ? function (array $ violations ) use ($ handlerFn ): void {
98
- $ this ->{$ handlerFn }($ violations );
112
+ ? function (array $ violations ) use ($ handlerFn, $ exception ): void {
113
+ $ this ->{$ handlerFn }($ violations, $ exception );
99
114
}
100
- : function (array $ violations ): void {
101
- throw new InvariantViolation (
115
+ : function (array $ violations ) use ($ exception ): void {
116
+ if (count ($ violations ) === 1 ) {
117
+ throw array_shift ($ violations );
118
+ }
119
+
120
+ throw new $ exception (
102
121
sprintf (
103
122
"Unable to create %s due %s " ,
104
123
basename (str_replace ('\\' , '/ ' , static ::class)),
0 commit comments