{ "uuid": [ { "value": "f5e8a084-7f6c-4ac4-8c99-8bd246d87ab5" } ], "langcode": [ { "value": "en" } ], "type": [ { "target_id": "daily_email", "target_type": "node_type", "target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7" } ], "revision_timestamp": [ { "value": "2025-04-21T01:21:38+00:00" } ], "revision_uid": [ { "target_type": "user", "target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849" } ], "revision_log": [], "status": [ { "value": true } ], "uid": [ { "target_type": "user", "target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849" } ], "title": [ { "value": "Which level is right for you?" } ], "created": [ { "value": "2024-02-20T00:00:00+00:00" } ], "changed": [ { "value": "2025-04-21T01:21:38+00:00" } ], "promote": [ { "value": false } ], "sticky": [ { "value": false } ], "default_langcode": [ { "value": true } ], "revision_translation_affected": [ { "value": true } ], "path": [ { "alias": "\/daily\/2024\/02\/20\/which-level-is-right-for-you", "langcode": null } ], "body": [ { "value": "\n

Today, whilst working on Versa<\/a>, I was experimenting with different PHPStan levels.<\/p>\n\n

Level 1 is the least strict level, and applies the fewest rules and returns the fewest results.<\/p>\n\n

As you increase the level, the stricter PHPStan is.<\/p>\n\n

Levelling up to 9<\/h2>\n\n

Here is the code to get the values of the --extra-args<\/code> and --working-dir<\/code> options:<\/p>\n\n

$extraArgs = $input->getOption('extra-args');\n$workingDir = $input->getOption('working-dir');\n<\/code><\/pre>\n\n

This passed PHPStan level 8, but these are the errors I get when running level 9:<\/p>\n\n

------ -------------------------------------------------------------------------------------------------------\n Line   versa\n------ -------------------------------------------------------------------------------------------------------\n 61     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 62     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 72     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 73     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 84     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 85     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 94     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 95     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 104    Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 105    Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 119    Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 120    Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n------ -------------------------------------------------------------------------------------------------------\n\n[ERROR] Found 12 errors\n<\/code><\/pre>\n\n

The issue is that $input->getOption()<\/code> from Symfony's InputInterface<\/code> returns mixed<\/code> - even though it always returns null<\/code> or a string.<\/p>\n\n

If I did --working-dir 2<\/code>, it would return the string \"2\"<\/code>.<\/p>\n\n

An empty string throws an error, and an empty value (if there are no extra arguments) returns null<\/code>.<\/p>\n\n

So, I know $workingDir<\/code> will always be a string and $extraArgs<\/code> will either a string or null<\/code>.<\/p>\n\n

Passing level 8<\/h2>\n\n

To pass level 8, PHPStan needs to be sure the variables are what I think they are.<\/p>\n\n

Here's the code I can use to get it to pass:<\/p>\n\n

$extraArgs = $input->getOption('extra-args');\n$workingDir = $input->getOption('working-dir');\nassert(is_string($extraArgs) || is_null($extraArgs));\nassert(is_string($workingDir));\n<\/code><\/pre>\n\n

By using assert()<\/code> and throwing an Exception if the condition fails, PHPStan is now happy with the code and my code passes level 9.<\/p>\n\n

Here's the thing<\/h2>\n\n

Although the extra code gets PHPStan to pass, it it worth it?<\/p>\n\n

Is this extra code adding value? Does it make the code more readable or is it likely to prevent a bug?<\/p>\n\n

In this case, I know the value will always be the type I expect.<\/p>\n\n

I can work around this using a baseline or annotations for PHPStan to ignore these lines, or is level 8 good enough for this project<\/strong>?<\/p>\n\n

Similar to 100% test coverage, is aiming for the highest PHPStan level an objective to be met, or is enough value being added added by the lower level?<\/p>\n\n

Which level is right for you and this codebase?<\/p>\n\n ", "format": "full_html", "processed": "\n

Today, whilst working on Versa<\/a>, I was experimenting with different PHPStan levels.<\/p>\n\n

Level 1 is the least strict level, and applies the fewest rules and returns the fewest results.<\/p>\n\n

As you increase the level, the stricter PHPStan is.<\/p>\n\n

Levelling up to 9<\/h2>\n\n

Here is the code to get the values of the --extra-args<\/code> and --working-dir<\/code> options:<\/p>\n\n

$extraArgs = $input->getOption('extra-args');\n$workingDir = $input->getOption('working-dir');\n<\/code><\/pre>\n\n

This passed PHPStan level 8, but these are the errors I get when running level 9:<\/p>\n\n

------ -------------------------------------------------------------------------------------------------------\n Line   versa\n------ -------------------------------------------------------------------------------------------------------\n 61     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 62     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 72     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 73     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 84     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 85     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 94     Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 95     Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 104    Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 105    Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n 119    Parameter $extraArgs of static method App\\Process\\Process::create() expects string|null, mixed given.\n 120    Parameter $workingDir of static method App\\Process\\Process::create() expects string, mixed given.\n------ -------------------------------------------------------------------------------------------------------\n\n[ERROR] Found 12 errors\n<\/code><\/pre>\n\n

The issue is that $input->getOption()<\/code> from Symfony's InputInterface<\/code> returns mixed<\/code> - even though it always returns null<\/code> or a string.<\/p>\n\n

If I did --working-dir 2<\/code>, it would return the string \"2\"<\/code>.<\/p>\n\n

An empty string throws an error, and an empty value (if there are no extra arguments) returns null<\/code>.<\/p>\n\n

So, I know $workingDir<\/code> will always be a string and $extraArgs<\/code> will either a string or null<\/code>.<\/p>\n\n

Passing level 8<\/h2>\n\n

To pass level 8, PHPStan needs to be sure the variables are what I think they are.<\/p>\n\n

Here's the code I can use to get it to pass:<\/p>\n\n

$extraArgs = $input->getOption('extra-args');\n$workingDir = $input->getOption('working-dir');\nassert(is_string($extraArgs) || is_null($extraArgs));\nassert(is_string($workingDir));\n<\/code><\/pre>\n\n

By using assert()<\/code> and throwing an Exception if the condition fails, PHPStan is now happy with the code and my code passes level 9.<\/p>\n\n

Here's the thing<\/h2>\n\n

Although the extra code gets PHPStan to pass, it it worth it?<\/p>\n\n

Is this extra code adding value? Does it make the code more readable or is it likely to prevent a bug?<\/p>\n\n

In this case, I know the value will always be the type I expect.<\/p>\n\n

I can work around this using a baseline or annotations for PHPStan to ignore these lines, or is level 8 good enough for this project<\/strong>?<\/p>\n\n

Similar to 100% test coverage, is aiming for the highest PHPStan level an objective to be met, or is enough value being added added by the lower level?<\/p>\n\n

Which level is right for you and this codebase?<\/p>\n\n ", "summary": null } ], "feeds_item": [ { "imported": "2025-04-21T01:21:38+00:00", "guid": null, "hash": "04a7f9d899ae8eb0f4c33ff32a7dabae", "target_type": "feeds_feed", "target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76" } ] }