1515 * read this tag; PhpStorm ignores it entirely (so its parser doesn't
1616 * trip on array shapes it doesn't understand).
1717 *
18- * Pairing invariant: every legacy `@method` must have a matching
19- * `@phpstan-method`. When an operation has no modeled input (or its input
20- * shape is empty), we still emit the unsealed empty shape `array{...}` so
21- * consumers and static-analysis tools see a uniform surface across all
18+ * Unless a service is explicitly excluded, every legacy `@method` has a
19+ * matching `@phpstan-method`. When an operation has no modeled input (or its
20+ * input shape is empty), we still emit the unsealed empty shape `array{...}`
21+ * so consumers and static-analysis tools see a uniform surface across all
2222 * operations.
2323 */
2424class ClientAnnotator
@@ -36,6 +36,15 @@ class ClientAnnotator
3636 private const GENERATED_ANNOTATION_PATTERN =
3737 '/^\* @(?:phpstan-)?method ( \\\\Aws \\\\Result| \\\\GuzzleHttp \\\\Promise \\\\Promise)[ <(]/ ' ;
3838
39+ /**
40+ * EC2's inline shapes make Ec2Client.php large enough that PHPStan may
41+ * not terminate when resolving the class. Keep its legacy annotations
42+ * while a more compact representation is investigated.
43+ */
44+ private const PHPSTAN_METHOD_EXCLUDED_ENDPOINTS = [
45+ 'ec2 ' ,
46+ ];
47+
3948 /** @var ReflectionClass */
4049 private $ reflection ;
4150
@@ -114,16 +123,17 @@ public function updateApiMethodAnnotations()
114123 }
115124
116125 /**
117- * Returns the list of docblock lines to emit. Each operation produces
118- * a `@method` line ( for PhpStorm + runtime baseline) immediately
119- * followed by a `@phpstan-method` line (or lines, for multi-line
120- * shapes) carrying the input array shape ( for PHPStan/Psalm). When
121- * the operation has no modeled input, we fall back to `array{...}`
122- * so the pair invariant always holds .
126+ * Returns the list of docblock lines to emit. Each operation produces a
127+ * `@method` line for PhpStorm and other IDEs. Unless the service is
128+ * excluded, it is immediately followed by a `@phpstan-method` line (or
129+ * lines, for multi-line shapes) carrying the input array shape for
130+ * PHPStan and Psalm. When the operation has no modeled input, we fall
131+ * back to `array{...}` .
123132 */
124133 private function getMethodAnnotations ()
125134 {
126135 $ annotations = [];
136+ $ emitPhpstanMethods = $ this ->shouldEmitPhpstanMethodAnnotations ();
127137 $ latestVersion = $ this ->getLatestVersion ();
128138
129139 foreach ($ this ->getMethods () as $ command => $ apiVersions ) {
@@ -132,29 +142,28 @@ private function getMethodAnnotations()
132142 "{$ command }Async " => '\\GuzzleHttp \\Promise \\Promise ' ,
133143 ];
134144
135- // Look up the input shape once per operation. Both the sync
136- // and async variants share the same input.
137- //
138- // When the operation has no modeled input (`input` key absent
139- // from api-2.json, e.g. AutoScaling::DescribeAdjustmentTypes,
140- // Acm::GetAccountConfiguration) or its input shape is empty,
141- // we still emit a paired `@phpstan-method` carrying the
142- // unsealed empty shape `array{...}`. Keeping the pair
143- // invariant means downstream consumers and the sweep tool
144- // never see a mixed `@method`/`@phpstan-method` surface.
145- $ inputShape = $ this ->resolveInputShape (
146- $ command ,
147- $ latestVersion ,
148- $ apiVersions
149- );
150- $ renderedShape = ($ inputShape !== null )
151- ? $ this ->getFormatter ($ latestVersion )->formatInput ($ inputShape )
152- : 'array{...} ' ;
153- if ($ renderedShape === null ) {
154- // Formatter returned null for an exotic shape (e.g. an
155- // input keyed to a non-structure type). Fall back to the
156- // unsealed empty shape so the pair invariant still holds.
157- $ renderedShape = 'array{...} ' ;
145+ if ($ emitPhpstanMethods ) {
146+ // Look up the input shape once per operation. Both the sync
147+ // and async variants share the same input.
148+ //
149+ // When the operation has no modeled input (`input` key absent
150+ // from api-2.json, e.g. AutoScaling::DescribeAdjustmentTypes,
151+ // Acm::GetAccountConfiguration) or its input shape is empty,
152+ // emit the unsealed empty shape `array{...}`.
153+ $ inputShape = $ this ->resolveInputShape (
154+ $ command ,
155+ $ latestVersion ,
156+ $ apiVersions
157+ );
158+ $ renderedShape = ($ inputShape !== null )
159+ ? $ this ->getFormatter ($ latestVersion )
160+ ->formatInput ($ inputShape )
161+ : 'array{...} ' ;
162+ if ($ renderedShape === null ) {
163+ // Formatter returned null for an exotic shape (e.g. an
164+ // input keyed to a non-structure type).
165+ $ renderedShape = 'array{...} ' ;
166+ }
158167 }
159168
160169 foreach ($ commandMethods as $ method => $ returnType ) {
@@ -164,12 +173,14 @@ private function getMethodAnnotations()
164173 $ apiVersions
165174 );
166175
167- foreach ($ this ->getPhpstanAnnotationLines (
168- $ method ,
169- $ returnType ,
170- $ renderedShape
171- ) as $ line ) {
172- $ annotations []= $ line ;
176+ if ($ emitPhpstanMethods ) {
177+ foreach ($ this ->getPhpstanAnnotationLines (
178+ $ method ,
179+ $ returnType ,
180+ $ renderedShape
181+ ) as $ line ) {
182+ $ annotations []= $ line ;
183+ }
173184 }
174185 }
175186 }
@@ -333,6 +344,15 @@ private function getLatestVersion(): string
333344 return end ($ versions );
334345 }
335346
347+ private function shouldEmitPhpstanMethodAnnotations (): bool
348+ {
349+ return !in_array (
350+ $ this ->getEndpoint (),
351+ self ::PHPSTAN_METHOD_EXCLUDED_ENDPOINTS ,
352+ true
353+ );
354+ }
355+
336356 private function getApiDefinition ($ version = 'latest ' )
337357 {
338358 $ provider = ApiProvider::defaultProvider ();
0 commit comments