You most likely have read my previous posts that go into detail regarding CakePHP and how to be most efficient in the IDE, as well as most "type safe" using PHPStan and static analyzing. See e.g. here.
In this post I want to share some more recent learnings and how I deal with this right now for the absolute best results.
Every command is usually shortcut aliased in composer scripts section.
And I added an alias to my ddev env:
hooks:
post-start:
- exec: echo 'alias c="composer"' >> ~/.bashrc
This way I can run composer update with c u
.
Running tools over modified subset
If you only touched 3-4 files, running the full tooling battery over all your code can be many minutes of unnecessary waiting time.
What if we ran those only over touched files?
This obviously works best for "per file" checks, and in our case "phpcs" (code style checking and fixing).
The other tools (phpstan, tests, annotations) can also miss side-effects to other unrelated files. So here it is a trade off between fast and reliable.
Often, if you have CI running, it would catch the side-effects and related but untouched files anyway.
So the trade-off can be worth it.
I want to show you how I manage e.g. phpcs here:
"scripts": {
...
"check": [
"@test",
"@stan",
"@cs-check",
"@annotations"
],
"cs-modified": "sh cs-modified.sh",
"cs-check": "phpcs --colors",
"cs-fix": "phpcbf --colors",
"stan": "phpstan analyze",
"test": "phpunit --colors=always",
"test-coverage": "phpdbg -qrrv vendor/bin/phpunit --coverage-html webroot/coverage/",
"annotations": "bin/cake annotate all -r",
"setup": "bin/cake generate code_completion && bin/cake generate phpstorm"
This is in my composer.json files of all projects.
Note the "cs-modified" script.
You can run c cs-m
to quickly fix CS issues in the files you touched.
On top of that you can do something similar with annotator, as well as "stan".
Only tests is a bit more tricky since the files touched on project level, might not be the same as in the tests.
Here is an example implementation:
#!/bin/bash
# Get list of modified and staged PHP files
MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB HEAD src/ tests/ config/ plugins/ | grep -E '\.php$')
# Exit if no PHP files are modified
if [ -z "$MODIFIED_FILES" ]; then
echo "No modified PHP files found."
exit 0
fi
echo "Running Code Sniffer Fixer on modified PHP files..."
vendor/bin/phpcbf $MODIFIED_FILES
echo "Checking remaining issues..."
vendor/bin/phpcs $MODIFIED_FILES
CI run
The CI, as outlined before, runs the full battery (read only):
- php composer.phar install --no-interaction
- vendor/bin/phpunit
- vendor/bin/phpstan
- vendor/bin/phpcs
- bin/cake annotate all -r -d --ci
The "annotate" CI availability is quite new.
Especially together with PHPStan this really helps to keep your false positive "green"s to a minimum, as without PHPStan reads your "outdated" annotations and based on that gives its OK.
Having the annotations aligned with the actual code and DB fields is crucial if you want PHPStan to find potential bugs after refactoring.
Pimp up your PHPStan
The default level 7 or 8 usually silently skip the undefined vars as well as mixed types.
This can lead to CI being happy but your code actually is broken. See the above blog posts for details.
So I usually install (on top of phpstan itself):
composer require --dev cakedc/cakephp-phpstan
composer require --dev rector/type-perfect
composer require --dev phpstan/phpstan-strict-rules
I recommend the following phpstan.neon setup:
includes:
- vendor/cakedc/cakephp-phpstan/extension.neon
- vendor/rector/type-perfect/config/extension.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
parameters:
level: 7
paths:
- src/
bootstrapFiles:
- config/bootstrap.php
type_perfect:
no_mixed_property: true
no_mixed_caller: true
treatPhpDocTypesAsCertain: false
ignoreErrors:
- identifier: missingType.generics
- identifier: missingType.iterableValue
# Can be ignored to find actual issues for now
- identifier: typePerfect.noArrayAccessOnObject
strictRules:
disallowedLooseComparison: false
booleansInConditions: false
booleansInLoopConditions: false
uselessCast: false
requireParentConstructorCall: false
disallowedBacktick: false
disallowedEmpty: false
disallowedImplicitArrayCreation: true # Here
disallowedShortTernary: false
overwriteVariablesWithLoop: false
closureUsesThis: false
matchingInheritedMethodNames: false
numericOperandsInArithmeticOperators: false
strictFunctionCalls: false
dynamicCallOnStaticMethod: true # Here
switchConditionsMatchingType: false
noVariableVariables: false
strictArrayFilter: false
illegalConstructorMethodCall: false
We mainly need disallowedImplicitArrayCreation
and dynamicCallOnStaticMethod
here.
Many of the others are actually not always good practice or overkill. So I ignore those.
If it is a new or quite clean project, I usually also enable a few more stricter checks, e.g.
type_perfect:
null_over_false: true
I hope this gave a bit of an overview on current tooling enhancements for your project to maximize development speed while also making sure PHPStan finds most of the issues right away that are harder to spot and might then hit you in production environment.