22
33namespace Fapi \HttpClient ;
44
5+ use Exception ;
56use Fapi \HttpClient \Utils \Json ;
67use Psr \Http \Message \RequestInterface ;
78use Psr \Http \Message \ResponseInterface ;
9+ use function array_keys ;
810use function array_shift ;
911use function assert ;
1012use function count ;
13+ use function implode ;
14+ use function ini_set ;
15+ use function preg_last_error ;
16+ use function preg_match ;
17+ use function preg_replace ;
18+ use function preg_replace_callback ;
19+ use function rtrim ;
20+ use function str_replace ;
1121use function strlen ;
1222use function substr ;
1323
@@ -20,6 +30,28 @@ class MockHttpClient implements IHttpClient
2030 /** @var array<ResponseInterface> */
2131 private array $ responses = [];
2232
33+ /** @var array<string,string> */
34+ public static array $ patterns = [
35+ '%% ' => '% ' , // one % character
36+ '%a% ' => '[^\r\n]+ ' , // one or more of anything except the end of line characters
37+ '%a\?% ' => '[^\r\n]* ' , // zero or more of anything except the end of line characters
38+ '%A% ' => '.+ ' , // one or more of anything including the end of line characters
39+ '%A\?% ' => '.* ' , // zero or more of anything including the end of line characters
40+ '%s% ' => '[\t ]+ ' , // one or more white space characters except the end of line characters
41+ '%s\?% ' => '[\t ]* ' , // zero or more white space characters except the end of line characters
42+ '%S% ' => '\S+ ' , // one or more of characters except the white space
43+ '%S\?% ' => '\S* ' , // zero or more of characters except the white space
44+ '%c% ' => '[^\r\n] ' , // a single character of any sort (except the end of line)
45+ '%d% ' => '[0-9]+ ' , // one or more digits
46+ '%d\?% ' => '[0-9]* ' , // zero or more digits
47+ '%i% ' => '[+-]?[0-9]+ ' , // signed integer value
48+ '%f% ' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)? ' , // floating point number
49+ '%h% ' => '[0-9a-fA-F]+ ' , // one or more HEX digits
50+ '%w% ' => '[0-9a-zA-Z_]+ ' , //one or more alphanumeric characters
51+ '%ds% ' => '[ \\\\/] ' , // directory separator
52+ '%(\[.+\][+*?{},\d]*)% ' => '$1 ' , // range
53+ ];
54+
2355 public function add (RequestInterface $ request , ResponseInterface $ response ): void
2456 {
2557 $ this ->requests [] = $ request ;
@@ -94,7 +126,7 @@ private function assertHttpRequestOptions(RequestInterface $expected, RequestInt
94126
95127 private function assertHttpRequestBody (RequestInterface $ expected , RequestInterface $ actual ): void
96128 {
97- if (( string ) $ expected ->getBody () === (string ) $ actual ->getBody ()) {
129+ if (self :: isMatching (( string ) $ expected ->getBody (), (string ) $ actual ->getBody () )) {
98130 return ;
99131 }
100132
@@ -116,4 +148,46 @@ private function formatUrl(string $url): string
116148 return $ url ;
117149 }
118150
151+ public static function isMatching (string $ pattern , string $ actual , bool $ strict = false ): bool
152+ {
153+ $ old = ini_set ('pcre.backtrack_limit ' , '10000000 ' );
154+
155+ if (!self ::isPcre ($ pattern )) {
156+ $ utf8 = (bool ) preg_match ('#\x80-\x{10FFFF}]#u ' , $ pattern ) ? 'u ' : '' ;
157+ $ suffix = ($ strict ? '$#DsU ' : '\s*$#sU ' ) . $ utf8 ;
158+ $ patterns = static ::$ patterns + [
159+ '[. \\\\+*?[^$(){|\#] ' => '\$0 ' , // preg quoting
160+ '\x00 ' => '\x00 ' ,
161+ '[\t ]*\r?\n ' => '[\t ]*\r?\n ' , // right trim
162+ ];
163+ $ pattern = '#^ ' . preg_replace_callback (
164+ '# ' . implode ('| ' , array_keys ($ patterns )) . '#U ' . $ utf8 ,
165+ static function ($ m ) use ($ patterns ) {
166+ foreach ($ patterns as $ re => $ replacement ) {
167+ $ s = preg_replace ("#^ $ re$#D " , str_replace ('\\' , '\\\\' , $ replacement ), $ m [0 ], 1 , $ count );
168+ if ((bool ) $ count ) {
169+ return $ s ;
170+ }
171+ }
172+ },
173+ rtrim ($ pattern , " \t\n\r" ),
174+ ) . $ suffix ;
175+ }
176+
177+ $ res = preg_match ($ pattern , $ actual );
178+ ini_set ('pcre.backtrack_limit ' , $ old );
179+ if ($ res === false || (bool ) preg_last_error ()) {
180+ throw new Exception (
181+ 'Error while executing regular expression. (PREG Error Code ' . preg_last_error () . ') ' ,
182+ );
183+ }
184+
185+ return (bool ) $ res ;
186+ }
187+
188+ private static function isPcre (string $ pattern ): bool
189+ {
190+ return (bool ) preg_match ('/^([~#]).+(\1)[imsxUu]*$/Ds ' , $ pattern );
191+ }
192+
119193}
0 commit comments