Fresh expert tips on CakePHP IDE and tooling best practices

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.