Total Complexity | 41 |
Total Lines | 215 |
Duplicated Lines | 0 % |
Changes | 3 | ||
Bugs | 0 | Features | 0 |
Complex classes like CustomAssertionTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use CustomAssertionTrait, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | rait CustomAssertionTrait |
||
24 | { |
||
25 | private static string $duration_regex = '/^(-?)P(?=.)((\d+)Y)?((\d+)M)?((\d+)D)?(T(?=.)((\d+)H)?((\d+)M)?(\d*(\.\d+)?S)?)?$/i'; |
||
26 | |||
27 | private static string $qname_regex = '/^[a-zA-Z_][\w.-]*:[a-zA-Z_][\w.-]*$/'; |
||
28 | |||
29 | private static string $ncname_regex = '/^[a-zA-Z_][\w.-]*$/'; |
||
30 | |||
31 | private static string $base64_regex = '/^(?:[a-z0-9+\/]{4})*(?:[a-z0-9+\/]{2}==|[a-z0-9+\/]{3}=)?$/i'; |
||
32 | |||
33 | private static string $uri_same_document_regex = '#^(?:\#([A-Za-z][A-Za-z0-9+\-.]*:(?:\/\/(?:(?:[A-Za-z0-9\-._~!$&\'()*+,;=:]|%[0-9A-Fa-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}|::(?:[0-9A-Fa-f]{1,4}:){5}|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::)(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)|[Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&\'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[A-Za-z0-9\-._~!$&\'()*+,;=]|%[0-9A-Fa-f]{2})*)(?::[0-9]*)?(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*|\/(?:(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)?|(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*|)(?:\?(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?(?:\#(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?|(?:\/\/(?:(?:[A-Za-z0-9\-._~!$&\'()*+,;=:]|%[0-9A-Fa-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}|::(?:[0-9A-Fa-f]{1,4}:){5}|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::)(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)|[Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&\'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[A-Za-z0-9\-._~!$&\'()*+,;=]|%[0-9A-Fa-f]{2})*)(?::[0-9]*)?(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*|\/(?:(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)?|(?:[A-Za-z0-9\-._~!$&\'()*+,;=@]|%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*|)(?:\?(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?(?:\#(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?))$#'; |
||
34 | |||
35 | private static string $urn_regex = '/\A(?i:urn:(?!urn:)(?<nid>[a-z0-9][a-z0-9-]{1,31}):(?<nss>(?:[-a-z0-9()+,.:=@;$_!*\'&~\/]|%[0-9a-f]{2})+)(?:\?\+(?<rcomponent>.*?))?(?:\?=(?<qcomponent>.*?))?(?:#(?<fcomponent>.*?))?)\z/'; |
||
36 | |||
37 | private static string $uri_regex = '#[A-Za-z][A-Za-z0-9+\-.]*:(?:\/\/(?:(?:[A-Za-z0-9\-._~!$&\'()*+,;=:]|%[0-9A-Fa-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}|::(?:[0-9A-Fa-f]{1,4}:){5}|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::)(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)|[Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&\'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[A-Za-z0-9\-._~!$&\'()*+,;=]|%[0-9A-Fa-f]{2})*)(?::[0-9]*)?(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*|\/(?:(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)?|(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*|)(?:\?(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?(?:\#(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?#'; |
||
38 | |||
39 | private static string $hostname_regex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/'; |
||
40 | |||
41 | /*********************************************************************************** |
||
42 | * NOTE: Custom assertions may be added below this line. * |
||
43 | * They SHOULD be marked as `private` to ensure the call is forced * |
||
44 | * through __callStatic(). * |
||
45 | * Assertions marked `public` are called directly and will * |
||
46 | * not handle any custom exception passed to it. * |
||
47 | ***********************************************************************************/ |
||
48 | |||
49 | |||
50 | /** |
||
51 | * @param string $value |
||
52 | */ |
||
53 | private static function validDuration(string $value, string $message = ''): void |
||
54 | { |
||
55 | if (filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$duration_regex]]) === false) { |
||
56 | throw new InvalidArgumentException(sprintf( |
||
57 | $message ?: '\'%s\' is not a valid xs:duration', |
||
58 | $value |
||
59 | )); |
||
60 | } |
||
61 | } |
||
62 | |||
63 | |||
64 | /** |
||
65 | * Note: This test is not bullet-proof but prevents a string containing illegal characters |
||
66 | * from being passed and ensures the string roughly follows the correct format for a Base64 encoded string |
||
67 | * |
||
68 | * @param string $value |
||
69 | * @param string $message |
||
70 | */ |
||
71 | private static function stringPlausibleBase64(string $value, string $message = ''): void |
||
72 | { |
||
73 | $result = true; |
||
74 | |||
75 | if (filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$base64_regex]]) === false) { |
||
76 | $result = false; |
||
77 | } elseif (strlen($value) % 4 !== 0) { |
||
78 | $result = false; |
||
79 | } else { |
||
80 | $decoded = base64_decode($value, true); |
||
81 | if ($decoded === false) { |
||
82 | $result = false; |
||
83 | } elseif (base64_encode($decoded) !== $value) { |
||
84 | $result = false; |
||
85 | } |
||
86 | } |
||
87 | |||
88 | if ($result === false) { |
||
89 | throw new InvalidArgumentException(sprintf( |
||
90 | $message ?: '\'%s\' is not a valid Base64 encoded string', |
||
91 | $value |
||
92 | )); |
||
93 | } |
||
94 | } |
||
95 | |||
96 | |||
97 | /** |
||
98 | * @param string $value |
||
99 | * @param string $message |
||
100 | */ |
||
101 | private static function validDateTime(string $value, string $message = ''): void |
||
102 | { |
||
103 | if ( |
||
104 | DateTimeImmutable::createFromFormat(DateTimeImmutable::ISO8601, $value) === false && |
||
105 | DateTimeImmutable::createFromFormat(DateTimeImmutable::RFC3339_EXTENDED, $value) === false |
||
106 | ) { |
||
107 | throw new InvalidArgumentException(sprintf( |
||
108 | $message ?: '\'%s\' is not a valid DateTime', |
||
109 | $value |
||
110 | )); |
||
111 | } |
||
112 | } |
||
113 | |||
114 | |||
115 | /** |
||
116 | * @param string $value |
||
117 | * @param string $message |
||
118 | */ |
||
119 | private static function validDateTimeZulu(string $value, string $message = ''): void |
||
120 | { |
||
121 | $dateTime1 = DateTimeImmutable::createFromFormat(DateTimeImmutable::ISO8601, $value); |
||
122 | $dateTime2 = DateTimeImmutable::createFromFormat(DateTimeImmutable::RFC3339_EXTENDED, $value); |
||
123 | |||
124 | $dateTime = $dateTime1 ?: $dateTime2; |
||
125 | if ($dateTime === false) { |
||
126 | throw new InvalidArgumentException(sprintf( |
||
127 | $message ?: '\'%s\' is not a valid DateTime', |
||
128 | $value |
||
129 | )); |
||
130 | } elseif ($dateTime->getTimezone()->getName() !== 'Z') { |
||
131 | throw new InvalidArgumentException(sprintf( |
||
132 | $message ?: '\'%s\' is not a DateTime expressed in the UTC timezone using the \'Z\' timezone identifier.', |
||
133 | $value |
||
134 | )); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | |||
139 | /** |
||
140 | * @param mixed $value |
||
141 | * @param array $values |
||
142 | * @param string $message |
||
143 | */ |
||
144 | private static function notInArray($value, array $values, string $message = ''): void |
||
145 | { |
||
146 | if (in_array($value, $values, true)) { |
||
147 | $callable = /** @param mixed $val */function ($val) { |
||
148 | return self::valueToString($val); |
||
149 | }; |
||
150 | |||
151 | throw new InvalidArgumentException(sprintf( |
||
152 | $message ?: 'Expected none of: %2$s. Got: %s', |
||
153 | self::valueToString($value), |
||
154 | implode(', ', array_map($callable, $values)), |
||
155 | )); |
||
156 | } |
||
157 | } |
||
158 | |||
159 | |||
160 | /** |
||
161 | * @param string $value |
||
162 | * @param string $message |
||
163 | */ |
||
164 | private static function validURN(string $value, string $message = ''): void |
||
165 | { |
||
166 | if (filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$urn_regex]]) === false) { |
||
167 | throw new InvalidArgumentException(sprintf( |
||
168 | $message ?: '\'%s\' is not a valid RFC8141 compliant URN', |
||
169 | $value |
||
170 | )); |
||
171 | } |
||
172 | } |
||
173 | |||
174 | |||
175 | /** |
||
176 | * @param string $value |
||
177 | * @param string $message |
||
178 | */ |
||
179 | private static function validURL(string $value, string $message = ''): void |
||
180 | { |
||
181 | if (filter_var($value, FILTER_VALIDATE_URL) === false) { |
||
182 | throw new InvalidArgumentException(sprintf( |
||
183 | $message ?: '\'%s\' is not a valid RFC2396 compliant URL', |
||
184 | $value |
||
185 | )); |
||
186 | } |
||
187 | } |
||
188 | |||
189 | |||
190 | /** |
||
191 | * @param string $value |
||
192 | * @param string $message |
||
193 | */ |
||
194 | private static function validURI(string $value, string $message = ''): void |
||
195 | { |
||
196 | if ( |
||
197 | filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$uri_regex]]) === false && |
||
198 | // We're very lenient here to accept DNS hostnames without a scheme |
||
199 | filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$hostname_regex]]) === false && |
||
200 | filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$uri_same_document_regex]]) === false |
||
201 | ) { |
||
202 | throw new InvalidArgumentException(sprintf( |
||
203 | $message ?: '\'%s\' is not a valid RFC3986 compliant URI', |
||
204 | $value |
||
205 | )); |
||
206 | } |
||
207 | } |
||
208 | |||
209 | |||
210 | /** |
||
211 | * @param string $value |
||
212 | * @param string $message |
||
213 | */ |
||
214 | private static function validNCName(string $value, string $message = ''): void |
||
215 | { |
||
216 | if (filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$ncname_regex]]) === false) { |
||
217 | throw new InvalidArgumentException(sprintf( |
||
218 | $message ?: '\'%s\' is not a valid non-colonized name (NCName)', |
||
219 | $value |
||
220 | )); |
||
221 | } |
||
222 | } |
||
223 | |||
224 | |||
225 | /** |
||
226 | * @param string $value |
||
227 | * @param string $message |
||
228 | */ |
||
229 | private static function validQName(string $value, string $message = ''): void |
||
230 | { |
||
231 | if ( |
||
232 | filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$qname_regex]]) === false && |
||
233 | filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => self::$ncname_regex]]) === false |
||
234 | ) { |
||
235 | throw new InvalidArgumentException(sprintf( |
||
236 | $message ?: '\'%s\' is not a valid qualified name (QName)', |
||
237 | $value |
||
238 | )); |
||
242 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.