{"id":37013890,"url":"https://github.com/struggle-for-php/sfp-psalm-typed-local-variable-plugin","last_synced_at":"2026-01-14T01:23:03.061Z","repository":{"id":40576469,"uuid":"282153664","full_name":"struggle-for-php/sfp-psalm-typed-local-variable-plugin","owner":"struggle-for-php","description":"finding mismatch type assignment in function/method scope with psalm.","archived":false,"fork":false,"pushed_at":"2023-01-09T12:02:09.000Z","size":69,"stargazers_count":16,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"0.2.x","last_synced_at":"2025-08-11T22:34:33.311Z","etag":null,"topics":["php","psalm","psalm-plugin"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/struggle-for-php.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-24T07:32:08.000Z","updated_at":"2024-01-19T10:19:04.000Z","dependencies_parsed_at":"2023-02-08T10:46:29.936Z","dependency_job_id":null,"html_url":"https://github.com/struggle-for-php/sfp-psalm-typed-local-variable-plugin","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/struggle-for-php/sfp-psalm-typed-local-variable-plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/struggle-for-php%2Fsfp-psalm-typed-local-variable-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/struggle-for-php%2Fsfp-psalm-typed-local-variable-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/struggle-for-php%2Fsfp-psalm-typed-local-variable-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/struggle-for-php%2Fsfp-psalm-typed-local-variable-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/struggle-for-php","download_url":"https://codeload.github.com/struggle-for-php/sfp-psalm-typed-local-variable-plugin/tar.gz/refs/heads/0.2.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/struggle-for-php%2Fsfp-psalm-typed-local-variable-plugin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28407691,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T00:40:43.272Z","status":"ssl_error","status_checked_at":"2026-01-14T00:40:42.636Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","psalm","psalm-plugin"],"created_at":"2026-01-14T01:23:02.207Z","updated_at":"2026-01-14T01:23:03.052Z","avatar_url":"https://github.com/struggle-for-php.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sfp-psalm-typed-local-variable-plugin\n\nfinding mismatch type assignment in function/method scope with [psalm](https://psalm.dev/).\n\n[![Packagist](https://img.shields.io/packagist/v/struggle-for-php/sfp-psalm-typed-local-variable-plugin.svg)](https://packagist.org/packages/struggle-for-php/sfp-psalm-typed-local-variable-plugin)\n[![Mutation testing badge](https://img.shields.io/endpoint?style=flat\u0026url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fstruggle-for-php%2Fsfp-psalm-typed-local-variable-plugin%2F0.2.x)](https://dashboard.stryker-mutator.io/reports/github.com/struggle-for-php/sfp-psalm-typed-local-variable-plugin/0.2.x)\n[![Psalm coverage](https://shepherd.dev/github/struggle-for-php/sfp-psalm-typed-local-variable-plugin/coverage.svg?)](https://shepherd.dev/github/struggle-for-php/sfp-psalm-typed-local-variable-plugin)\n\n## Installation\n```\n$ composer require --dev struggle-for-php/sfp-psalm-typed-local-variable-plugin\n$ vendor/bin/psalm-plugin enable struggle-for-php/sfp-psalm-typed-local-variable-plugin\n```\n\nlatest version supports psalm `^4`.\n\n## Demo\n\n```php\n\u003c?php\nclass Entity{}\ninterface Repository\n{\n    public function findOneById(int $id): ?Entity;\n}\ninterface Mock{}\n/** @return \\DateTimeInterface\u0026Mock */\nfunction date_mock() {\n    return new class('now') extends \\DateTime implements Mock{};\n}\n\nclass Demo\n{\n    /** @var Repository */\n    private $repository;\n\n    function typed_by_phpdoc() : void\n    {\n        /** @var string|null $nullable_string */\n        $nullable_string = null;\n        $nullable_string = \"a\";\n        $nullable_string = true; // ERROR\n    }\n\n    function typed_by_assignement() : void\n    {\n        $date = new \\DateTimeImmutable('now');\n        if (\\rand() % 2 === 0) {\n            $date = new \\DateTime('tomorrow'); // ERROR\n        }\n\n        $bool = true; //direct typed without doc-block\n        $bool = false; // ok (currently, this plugin treats true|false as bool)\n        $bool = 1; // ERROR\n    }\n\n    function mismatch_by_return() : void\n    {\n        /** @var Entity $entity */\n        $entity = $this-\u003erepository-\u003efindOneById(1); // ERROR\n    }\n\n    function works_with_intersection() : void\n    {\n        /** @var \\DateTimeInterface\u0026Mock $date */\n        $date = new \\DateTime('now'); // ERROR\n        $date = date_mock(); // success\n    }\n}\n```\n\n\n```bash\n$ ./vendor/bin/psalm -c demo.psalm.xml\nScanning files...\nAnalyzing files...\n\nE\n\nERROR: InvalidScalarTypedLocalVariableIssue - demo/demo.php:23:28 - Type true should be a subtype of null|string\n        $nullable_string = true; // ERROR\n\n\nERROR: InvalidTypedLocalVariableIssue - demo/demo.php:30:21 - Type DateTime should be a subtype of DateTimeImmutable\n            $date = new \\DateTime('tomorrow'); // ERROR\n\n\nERROR: InvalidScalarTypedLocalVariableIssue - demo/demo.php:35:17 - Type 1 should be a subtype of bool\n        $bool = 1; // ERROR\n\n\nERROR: InvalidTypedLocalVariableIssue - demo/demo.php:41:19 - Type Entity|null should be a subtype of Entity\n        $entity = $this-\u003erepository-\u003efindOneById(1); // ERROR\n\n\nERROR: InvalidTypedLocalVariableIssue - demo/demo.php:47:17 - Type DateTime should be a subtype of DateTimeInterface\u0026Mock\n        $date = new \\DateTime('now'); // ERROR\n\n\n------------------------------\n5 errors found\n------------------------------\n```\n\n## Plugin Issues\n\nAll issue names has `TypedLocalVariableIssue` suffix.\n\n* MixedTypeCoercionTypedLocalVariableIssue\n  * nearly [MixedArgumentTypeCoercion](https://psalm.dev/docs/running_psalm/issues/MixedArgumentTypeCoercion)\n\neg.  \n```php\nfunction foo(array $a) : void {\n    /** @var string[] $x */\n    $x = $a;\n}\n```\n\n* TypeCoercionTypedLocalVariableIssue\n  * nearly [ArgumentTypeCoercion](https://psalm.dev/docs/running_psalm/issues/ArgumentTypeCoercion)\n\neg.\n```php\nclass A {}\nclass B extends A {}\n\nfunction takesA(A $a) : void {\n    /** @var B $b */\n    $b = $a;\n}\n```\n\n* InvalidScalarTypedLocalVariableIssue\n  * nearly [InvalidScalarArgument](https://psalm.dev/docs/running_psalm/issues/InvalidScalarArgument/)\n\n* InvalidTypedLocalVariableIssue\n  * nearly [InvalidArgument](https://psalm.dev/docs/running_psalm/issues/InvalidArgument/)\n\nIf you want **suppress** specific issue, please setting `psalm.xml` like below.\n\n```xml\n\u003cissueHandlers\u003e\n  \u003cPluginIssue name=\"MixedTypeCoercionTypedLocalVariableIssue\"\u003e\n      \u003cerrorLevel type=\"suppress\"\u003e\n          \u003cfile name=\"src/Foo.php\"/\u003e\n      \u003c/errorLevel\u003e\n  \u003c/PluginIssue\u003e\n\u003c/issueHandlers\u003e\n```\n\n## Disclaimer\nThis is **Experimental** plugin.\n\n## Limitation\n\n* NOT support global variables.\n* NOT support variables in namespace.\n* NOT support [Variable variables](https://php.net/language.variables.variable)\n* Non-each inline VariableReference.\n  * eg.\n```php\n/** @var string $var1 */\n/** @var bool $var2 */\n$var1 = 'string'; // cannot determine type for $var1\n\n// should fix like below\n/** @var string $var1 */\n$var1 = 'string';\n/** @var bool $var2 */\n$var2 = true;\n```\n\n## Todo\n- [ ] optional setting for only from_docblock typed.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstruggle-for-php%2Fsfp-psalm-typed-local-variable-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstruggle-for-php%2Fsfp-psalm-typed-local-variable-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstruggle-for-php%2Fsfp-psalm-typed-local-variable-plugin/lists"}