11<?php
2+
23namespace Codeception \Util ;
34
45/**
@@ -59,7 +60,6 @@ public function __construct($jsonArray)
5960 * return strpos(' ', $value) !== false;
6061 * });
6162 * // => use it as 'string:slug'
62-
6363 *
6464 * // add custom function to matcher with `len($val)` syntax
6565 * // parameter matching patterns should be valid regex and start with `/` char
@@ -70,7 +70,7 @@ public function __construct($jsonArray)
7070 * ?>
7171 * ```
7272 *
73- * @param $name
73+ * @param $name
7474 * @param callable $callable
7575 */
7676 public static function addCustomFilter ($ name , callable $ callable )
@@ -119,35 +119,64 @@ protected function typeComparison($data, $jsonType)
119119 if (!array_key_exists ($ key , $ data )) {
120120 return "Key ` $ key` doesn't exist in " . json_encode ($ data );
121121 }
122+
122123 if (is_array ($ jsonType [$ key ])) {
123124 $ message = $ this ->typeComparison ($ data [$ key ], $ jsonType [$ key ]);
125+
124126 if (is_string ($ message )) {
125127 return $ message ;
126128 }
129+
127130 continue ;
128131 }
129- $ matchTypes = preg_split ("#(?![^]\(]*\))\|# " , $ type );
130- $ matched = false ;
132+
133+ $ regexMatcher = '/:regex\((.*?)\)(?:\|(boolean|integer|double|float|string|array|object|resource|resource \(closed\)|null|unknown type)|$)/ ' ;
134+ $ regexes = [];
135+
136+ // Match the string ':regex(' and any characters until a regex delimiter (matches 99.999% use cases) followed by character ')'
137+ // Place the 'any character' + delimiter matches in to an array.
138+ preg_match_all ($ regexMatcher , $ type , $ regexes );
139+
140+ // Do the same match as above, but replace the the 'any character' + delimiter with a place holder ($${count}).
141+ $ filterType = preg_replace_callback ($ regexMatcher , function () {
142+ static $ count = 0 ;
143+ return ':regex($$ ' . $ count ++ . ') ' ;
144+ }, $ type );
145+
146+ $ matchTypes = preg_split ("#(?![^]\(]*\))\|# " , $ filterType );
147+ $ matched = false ;
131148 $ currentType = strtolower (gettype ($ data [$ key ]));
132- if ($ currentType == 'double ' ) {
149+
150+ if ($ currentType === 'double ' ) {
133151 $ currentType = 'float ' ;
134152 }
153+
135154 foreach ($ matchTypes as $ matchType ) {
136- $ filters = preg_split ("#(?![^]\(]*\))\:# " , $ matchType );
137- $ expectedType = trim ( strtolower (array_shift ($ filters )));
155+ $ filters = preg_split ("#(?![^]\(]*\))\:# " , $ matchType );
156+ $ expectedType = strtolower ( trim (array_shift ($ filters )));
138157
139- if ($ expectedType != $ currentType ) {
158+ if ($ expectedType !== $ currentType ) {
140159 continue ;
141160 }
161+
142162 $ matched = true ;
143163
144164 foreach ($ filters as $ filter ) {
165+ // Fill regex pattern back into the filter.
166+ $ filter = preg_replace_callback ('/\$\$\d+/ ' , function ($ m ) use ($ regexes ) {
167+ $ pos = (int )substr ($ m [0 ], 2 );
168+
169+ return $ regexes [1 ][$ pos ];
170+ }, $ filter );
171+
145172 $ matched = $ matched && $ this ->matchFilter ($ filter , $ data [$ key ]);
146173 }
174+
147175 if ($ matched ) {
148176 break ;
149177 }
150178 }
179+
151180 if (!$ matched ) {
152181 return sprintf ("` $ key: %s` is of type ` $ type` " , var_export ($ data [$ key ], true ));
153182 }
@@ -189,7 +218,8 @@ protected function matchFilter($filter, $value)
189218 }
190219 if ($ filter === 'email ' ) { // from http://emailregex.com/
191220 // @codingStandardsIgnoreStart
192- return preg_match ('/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD ' , $ value );
221+ return preg_match ('/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD ' ,
222+ $ value );
193223 // @codingStandardsIgnoreEnd
194224 }
195225 if ($ filter === 'empty ' ) {
0 commit comments