Skip to main content

php-type-errors

Use when PHPStan or Psalm reports a type error on PHP code: resolving it with a less-strict type that satisfies both tools, declaring template parameters, the MixedAssignment and invariant-generic patterns, and tool-specific docblocks as the last resort.

설치로 이동

소스 정보

저장소
ScientaNL/DoctrineJsonFunctions
최근 소스 활동
2026년 9월 16일 07:17
감지된 SKILL.md 언어
영어
스타
591
포크
51

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
php-type-errors
description
Use when PHPStan or Psalm reports a type error on PHP code: resolving it with a less-strict type that satisfies both tools, declaring template parameters, the MixedAssignment and invariant-generic patterns, and tool-specific docblocks as the last resort.
# PHPStan and Psalm type errors Both tools gate a change and must pass simultaneously (`composer phpstan`, `composer psalm`). Proactive PHP conventions (docblocks, promotion, array types): [rules/php-quality.md](../../rules/php-quality.md). Whether to suppress at all: [rules/code-quality.md § Suppression Policy](../../rules/code-quality.md#suppression-policy). Unfamiliar error identifier → read `https://phpstan.org/error-identifiers/<identifier>` before guessing. ## Prefer a less-strict type that satisfies both tools When an annotation satisfies one tool but not the other, loosen it rather than writing parallel `@phpstan-`/`@psalm-` docblocks: one annotation, easier to maintain, usually matches real call-site usage. - Check actual call-site usage before tightening; if call sites vary, loosen the annotation rather than changing dozens of callers. - `array<array-key, mixed>` not `array<int, mixed>`: survives inheritance, both tools accept. - `array<mixed>` when call sites mix enums, associative, and list shapes. - Flexible property types on base classes children extend: `@var array<array-key, string>`, not `array{0: string, 1: string}|array{}`. Applies to: abstract nodes with per-platform implementations that disagree, constructors with varied params, properties inherited by many children, any case where PHPStan passes but Psalm reports an inheritance or usage conflict. ## `MissingTemplateParam`: declare the parameter explicitly Declare the parameter on every link in the chain: the interface, the implementation, and each concrete child of a generic base. ```php /** @template T of \Doctrine\ORM\Query\AST\Node */ abstract class NodeCollection { /** @param list<T> $nodes */ public function __construct(protected array $nodes) {} } /** @template-extends NodeCollection<\Doctrine\ORM\Query\AST\PathExpression> */ final class PathExpressionCollection extends NodeCollection {} ``` ## Psalm `MixedAssignment`: pass the mixed value, don't assign it The check fires on assigning a `mixed` expression to a variable, never on passing one to a parameter declared `mixed`. Narrow an untyped source (a decoded JSON payload, a raw driver result row) by handing it straight to a helper: ```php // ✅ no suppression needed return self::asNonEmptyString($row['data']); private static function asNonEmptyString(mixed $value): string | null { return is_string($value) && $value !== '' ? $value : null; } // ❌ reaching for the suppression first /** @psalm-suppress MixedAssignment */ $data = $row['data']; ``` Go to the helper first for an array offset: `is_string($arr['k'] ?? null)` does not narrow the offset, so keeping the guard inline just moves the error to `MixedArgument` on the next call. ## `Assert::allPositiveInteger` narrows for PHPStan, not Psalm Satisfying a `list<positive-int>` parameter from a `list<int>` via inline `Assert::allPositiveInteger($list)` passes PHPStan (phpstan-webmozart-assert) but leaves Psalm reporting `ArgumentTypeCoercion`. Narrow at the source (`@return positive-int` on the producing method and `@param list<positive-int>` on the intermediate), not at the inline `all*` assert at the call. ## Invariant generic looks like a false positive PHPStan reports `argument.type` with identical types on both sides and `💡 Template type T is not covariant`. Not a false positive: the hint is the diagnosis: an invariant `@template T` can't prove exact type identity when a variable annotated `Foo<X>` is passed to another `Foo<X>` parameter. | Approach | Verdict | |---|---| | Remove the generic annotation (leave the raw PHP type) | ✅ preferred first attempt: accepts any instantiation | | Declare `@template-covariant T` on the class or interface you own | ✅ best long-term fix | | `@param Foo<covariant T>` (generic tag) | ❌ Psalm parses `covariant` as a namespace | | `@phpstan-param Foo<covariant T>` + `@psalm-param Foo<T>` | ✅ last resort only | **Never** `@param Foo<covariant T>` in `src/`: Psalm misparses `covariant` regardless of PHPStan. ## Tool-specific docblock tags (last resort) Only when one tool needs syntax that crashes the other (PHPStan `covariant`, star projection, call-site variance). Write parallel tags: Psalm reads `@psalm-*` and ignores `@phpstan-*`; PHPStan the reverse. Drop the plain `@param`/`@return` to avoid ambiguity. ```php /** * @phpstan-param Collection<covariant Node> $nodes * @psalm-param Collection<Node> $nodes */ ``` ## Registered suppressions already in this repo `psalm.xml` holds the registered `<issueHandlers>`; read it before concluding an issue is unsilenced. A new entry there is a suppression like any other and goes through the [suppression policy](../../rules/code-quality.md#suppression-policy). `findUnusedBaselineEntry` is on, so a stale entry fails the run.
GitHub에서 보기