@@ -37,12 +37,16 @@ class Property extends AbstractProperty
3737 /** @var Validator[] */
3838 protected $ validators = [];
3939 /** @var Schema */
40- protected $ schema ;
40+ protected $ nestedSchema ;
4141 /** @var PropertyDecoratorInterface[] */
4242 public $ decorators = [];
4343 /** @var TypeHintDecoratorInterface[] */
4444 public $ typeHintDecorators = [];
4545
46+ private $ renderedTypeHints = [];
47+ /** @var int Track the amount of unresolved validators */
48+ private $ pendingValidators = 0 ;
49+
4650 /**
4751 * Property constructor.
4852 *
@@ -59,6 +63,8 @@ public function __construct(string $name, ?PropertyType $type, JsonSchema $jsonS
5963
6064 $ this ->type = $ type ;
6165 $ this ->description = $ description ;
66+
67+ $ this ->resolve ();
6268 }
6369
6470 /**
@@ -94,26 +100,47 @@ public function setType(PropertyType $type = null, PropertyType $outputType = nu
94100 /**
95101 * @inheritdoc
96102 */
97- public function getTypeHint (bool $ outputType = false ): string
103+ public function getTypeHint (bool $ outputType = false , array $ skipDecorators = [] ): string
98104 {
105+ if (isset ($ this ->renderedTypeHints [$ outputType ])) {
106+ return $ this ->renderedTypeHints [$ outputType ];
107+ }
108+
109+ static $ skipDec = [];
110+
111+ $ additionalSkips = array_diff ($ skipDecorators , $ skipDec );
112+ $ skipDec = array_merge ($ skipDec , $ additionalSkips );
113+
99114 $ input = [$ outputType && $ this ->outputType !== null ? $ this ->outputType : $ this ->type ];
100115
101116 // If the output type differs from an input type also accept the output type
102117 if (!$ outputType && $ this ->outputType !== null && $ this ->outputType !== $ this ->type ) {
103118 $ input = [$ this ->type , $ this ->outputType ];
104119 }
105120
106- $ input = join ('| ' , array_filter (array_map (function (?PropertyType $ input ) use ($ outputType ): string {
107- $ typeHint = $ input ? $ input ->getName () : '' ;
121+ $ input = join (
122+ '| ' ,
123+ array_filter (array_map (function (?PropertyType $ input ) use ($ outputType , $ skipDec ): string {
124+ $ typeHint = $ input ? $ input ->getName () : '' ;
108125
109- foreach ($ this ->typeHintDecorators as $ decorator ) {
110- $ typeHint = $ decorator ->decorate ($ typeHint , $ outputType );
111- }
126+ $ filteredDecorators = array_filter (
127+ $ this ->typeHintDecorators ,
128+ static function (TypeHintDecoratorInterface $ decorator ) use ($ skipDec ): bool {
129+ return !in_array (get_class ($ decorator ), $ skipDec );
130+ }
131+ );
132+
133+ foreach ($ filteredDecorators as $ decorator ) {
134+ $ typeHint = $ decorator ->decorate ($ typeHint , $ outputType );
135+ }
136+
137+ return $ typeHint ;
138+ }, $ input ))
139+ );
112140
113- return $ typeHint ;
114- }, $ input )));
141+ $ skipDec = array_diff ($ skipDec , $ additionalSkips );
115142
116- return $ input ?: 'mixed ' ;
143+ return $ this -> renderedTypeHints [ $ outputType ] = $ input ?: 'mixed ' ;
117144 }
118145
119146 /**
@@ -139,6 +166,18 @@ public function getDescription(): string
139166 */
140167 public function addValidator (PropertyValidatorInterface $ validator , int $ priority = 99 ): PropertyInterface
141168 {
169+ if (!$ validator ->isResolved ()) {
170+ $ this ->isResolved = false ;
171+
172+ $ this ->pendingValidators ++;
173+
174+ $ validator ->onResolve (function () {
175+ if (--$ this ->pendingValidators === 0 ) {
176+ $ this ->resolve ();
177+ }
178+ });
179+ }
180+
142181 $ this ->validators [] = new Validator ($ validator , $ priority );
143182
144183 return $ this ;
@@ -169,7 +208,7 @@ public function getOrderedValidators(): array
169208 {
170209 usort (
171210 $ this ->validators ,
172- function (Validator $ validator , Validator $ comparedValidator ) {
211+ static function (Validator $ validator , Validator $ comparedValidator ): int {
173212 if ($ validator ->getPriority () == $ comparedValidator ->getPriority ()) {
174213 return 0 ;
175214 }
@@ -178,7 +217,7 @@ function (Validator $validator, Validator $comparedValidator) {
178217 );
179218
180219 return array_map (
181- function (Validator $ validator ) {
220+ static function (Validator $ validator ): PropertyValidatorInterface {
182221 return $ validator ->getValidator ();
183222 },
184223 $ this ->validators
@@ -274,7 +313,7 @@ public function isReadOnly(): bool
274313 */
275314 public function setNestedSchema (Schema $ schema ): PropertyInterface
276315 {
277- $ this ->schema = $ schema ;
316+ $ this ->nestedSchema = $ schema ;
278317 return $ this ;
279318 }
280319
@@ -283,7 +322,7 @@ public function setNestedSchema(Schema $schema): PropertyInterface
283322 */
284323 public function getNestedSchema (): ?Schema
285324 {
286- return $ this ->schema ;
325+ return $ this ->nestedSchema ;
287326 }
288327
289328 /**
0 commit comments