2323
2424final class OpenApiResponseValidator
2525{
26+ /** @var array<string, OpenApiPathMatcher> */
27+ private array $ pathMatchers = [];
28+ private Validator $ opisValidator ;
29+ private ErrorFormatter $ errorFormatter ;
30+
2631 public function __construct (
2732 private readonly int $ maxErrors = 20 ,
2833 ) {
@@ -31,6 +36,13 @@ public function __construct(
3136 sprintf ('maxErrors must be 0 (unlimited) or a positive integer, got %d. ' , $ this ->maxErrors ),
3237 );
3338 }
39+
40+ $ resolvedMaxErrors = $ this ->maxErrors === 0 ? PHP_INT_MAX : $ this ->maxErrors ;
41+ $ this ->opisValidator = new Validator (
42+ max_errors: $ resolvedMaxErrors ,
43+ stop_at_first_error: $ resolvedMaxErrors === 1 ,
44+ );
45+ $ this ->errorFormatter = new ErrorFormatter ();
3446 }
3547
3648 public function validate (
@@ -47,7 +59,7 @@ public function validate(
4759
4860 /** @var string[] $specPaths */
4961 $ specPaths = array_keys ($ spec ['paths ' ] ?? []);
50- $ matcher = new OpenApiPathMatcher ( $ specPaths , OpenApiSpecLoader:: getStripPrefixes () );
62+ $ matcher = $ this -> getPathMatcher ( $ specName , $ specPaths );
5163 $ matchedPath = $ matcher ->match ($ requestPath );
5264
5365 if ($ matchedPath === null ) {
@@ -135,19 +147,13 @@ public function validate(
135147 $ schemaObject = self ::toObject ($ jsonSchema );
136148 $ dataObject = self ::toObject ($ responseBody );
137149
138- $ resolvedMaxErrors = $ this ->maxErrors === 0 ? PHP_INT_MAX : $ this ->maxErrors ;
139- $ validator = new Validator (
140- max_errors: $ resolvedMaxErrors ,
141- stop_at_first_error: $ resolvedMaxErrors === 1 ,
142- );
143- $ result = $ validator ->validate ($ dataObject , $ schemaObject );
150+ $ result = $ this ->opisValidator ->validate ($ dataObject , $ schemaObject );
144151
145152 if ($ result ->isValid ()) {
146153 return OpenApiValidationResult::success ($ matchedPath );
147154 }
148155
149- $ formatter = new ErrorFormatter ();
150- $ formattedErrors = $ formatter ->format ($ result ->error ());
156+ $ formattedErrors = $ this ->errorFormatter ->format ($ result ->error ());
151157
152158 $ errors = [];
153159 foreach ($ formattedErrors as $ path => $ messages ) {
@@ -187,6 +193,14 @@ private static function toObject(mixed $value): mixed
187193 return $ object ;
188194 }
189195
196+ /**
197+ * @param string[] $specPaths
198+ */
199+ private function getPathMatcher (string $ specName , array $ specPaths ): OpenApiPathMatcher
200+ {
201+ return $ this ->pathMatchers [$ specName ] ??= new OpenApiPathMatcher ($ specPaths , OpenApiSpecLoader::getStripPrefixes ());
202+ }
203+
190204 /**
191205 * Find the first JSON-compatible content type from the response spec.
192206 *
0 commit comments