zebra-north/phpcs-short-types
A PHP_CodeSniffer ruleset that enforces short, consistent type declarations in PHP code. It flags long or legacy type names and helps keep type hints and annotations concise across your codebase, with easy integration into existing PHPCS setups and CI pipelines.
Read the release note before upgrading. v1.1.0 is not a feature release — it is a compatibility rewrite. The note ("CodeSniffer v4 has changed internally so the previous hack no longer works… the only way now is to replace the faulty sniffs with modified versions") tells you two things: the package now tracks PHPCS 4 internals, and it has changed approach from whatever patching trick it used before to shipping modified copies of sniffs. Both are breaking for a consuming project, so treat 1.0.x → 1.1.x as a coordinated PHPCS upgrade, not a composer update.
Minimal install (fresh project, PHPCS 4)
composer require --dev zebra-north/phpcs-short-types \
squizlabs/php_codesniffer \
dealerdirect/phpcodesniffer-composer-installer
Let Composer resolve PHPCS rather than pinning it yourself first — the package's constraint is now the authority on which PHPCS major it supports, and that is exactly the information you want out of the resolver.
Allow the installer plugin and commit the answer so CI does not block:
{
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
Upgrade path from 1.0.x (or from PHPCS 3.x)
composer show zebra-north/phpcs-short-types # read the php_codesniffer constraint first
composer why squizlabs/php_codesniffer # confirm nothing else is holding you on 3.x
Do the PHPCS 3 → 4 bump as its own PR with no other changes, get a green build with your existing standard, then move to 1.1.0. Mixing the two makes every resulting diff ambiguous.
Where to look first
composer show zebra-north/phpcs-short-types — the require block is the only reliable statement of which PHPCS major v1.1.0 supports. The release note implies 4.x; verify.vendor/bin/phpcs -i — confirm the standard name is registered. Still the first thing that breaks.vendor/bin/phpcs -e --standard=<StandardName> — more important than before. Because 1.1.0 "replaces the faulty sniffs", the inventory will contain core sniff codes (Squiz.Commenting.FunctionComment, VariableComment, and friends) rather than one package-specific code. Diff that list against a pre-upgrade phpcs -e and you will see exactly what is being shadowed.src/Sniffs/ — it is now a set of modified upstream sniff copies rather than a single self-contained class. Read the diff against the matching vendor/squizlabs/php_codesniffer/src/Standards/Squiz/Sniffs/Commenting/*.php files; that diff is the package's behaviour.First use case — same one-shot sweep, but run it before and after the upgrade and compare --report=source totals. If the counts change on unchanged code, a replaced sniff has altered behaviour and you need to know that before it hits CI.
vendor/bin/phpcs --standard=<StandardName> --report=source app/
vendor/bin/phpcbf --standard=<StandardName> app/
Pin the pair, not just the package. Since 1.1.0 is glued to PHPCS 4 internals, the only safe lockfile entries are an explicit PHPCS major plus an exact package version:
"require-dev": {
"squizlabs/php_codesniffer": "^4.0",
"zebra-north/phpcs-short-types": "1.1.0"
}
Bumping either one alone is the failure mode this release introduces. Renovate/Dependabot should group them so they always move together.
Standalone vs. composed standard. The old advice still holds, with one addition: because the package now shadows core sniffs, a composed ruleset that references Squiz.Commenting and this standard may include the same sniff code twice. Depending on registration order you get either the modified version (intended) or duplicate diagnostics (not). Verify with phpcs -e and, if needed, drop the conflicting Squiz.Commenting.* refs from your own ruleset and let the package supply them.
"scripts": {
"lint": "phpcs",
"lint:types": "phpcs --standard=<StandardName>",
"lint:fix": "phpcbf",
"check": ["@lint", "@lint:types"]
}
CI as a blocking gate, fixer as a local convenience. Unchanged: phpcs read-only in CI, phpcbf locally. Add a smoke assertion that the standard actually loaded, because a silently unregistered standard turns CI green:
- run: composer install --no-progress
- run: vendor/bin/phpcs -i | grep -q '<StandardName>'
- run: vendor/bin/phpcs --standard=<StandardName> --report=full --no-cache
Pre-commit hook. Still a near-ideal candidate — cheap, deterministic, fixable on already-correct code. Run phpcbf on staged files only, then re-stage. Nothing about 1.1.0 changes this.
Editor wiring. Point the VS Code phpcs extension or PhpStorm at vendor/bin/phpcs and at your project phpcs.xml. Keep PhpStorm's "Override standards" off. If you have editors on mixed PHPCS versions across a team, expect disagreement on which sniff actually ran — pin per-project.
Ordering against other sniffers. Still run the short-types pass before docblock alignment (Slevomat, Squiz.Commenting alignment, php-cs-fixer align_phpdoc), because boolean → bool shortens lines and shifts alignment. This matters more now: if the package replaces an alignment-affecting sniff, the interaction is internal rather than across packages, and a second phpcbf pass may be needed to settle.
Gradual adoption on a legacy tree. Combine <exclude-pattern> with --report=source and convert directory by directory. On the 1.x → 1.1.0 upgrade specifically, do not combine the migration with the rollout — land the toolchain bump first, confirm --report=source totals are unchanged on a clean tree, then resume widening scope.
Paired with static analysis. Run phpcs before PHPStan/Psalm so the cheap fixable check fails fast. int/bool are canonical for both, so this sniff mostly prevents hybrid annotations static analysers normalise inconsistently.
v1.1.0 is a hard fork of upstream sniff code, with the maintenance cost that implies. "Replace the faulty sniffs with modified versions" means the package now vendors (modified) PHPCS internals. Those internals are not a public API — PHPCS 4.1, 4.2, 5.0 can each change token positions, helper signatures, or the sniff base class, and the package will need a matching release each time. Budget for this: pin exactly, and treat every PHPCS bump as "does the sniff still load and still fire?" Test on a fixture file with known-bad docblocks, not just a green pipeline.
Sniff codes in your suppressions are probably core codes now. phpcs -s on a violation to get the code, then // phpcs:ignore <code>. If that code is Squiz.Commenting.FunctionComment, your ignore comment also suppresses the original sniff's diagnostics on that line — including genuine problems. Prefer file-scoped <exclude-pattern> for deliberate holdouts, and re-check phpcs:ignore comments during the upgrade, since the codes they reference may have changed.
Verify shadowing, not just installation. phpcs -i proves the standard registered. phpcs -e proves which sniffs are active. Diff the inventory before and after 1.1.0; if a core sniff disappeared or was swapped, that is a behavioural change to your whole standard, not just to short-types handling.
The standard will not load without the Composer installer plugin. Still the most common setup failure: require, then phpcs -i shows nothing. Fix with dealerdirect/phpcodesniffer-composer-installer, or manually vendor/bin/phpcs --config-set installed_paths vendor/zebra-north/phpcs-short-types — remembering that writes to the global config unless run from the project root, so prefer the plugin in CI.
It still only inspects docblocks. @param, @return, @var, @property, @method, inline annotations. It cannot touch real PHP declarations, string defaults, YAML fixtures, or @phpstan-type aliases. Scope expectations accordingly.
Fixture-test capitalisation and aliases explicitly. Boolean, Integer, Double, Real, boolean|null, array<integer>. Replaced-sniff behaviour is inherited from upstream and then modified — exactly the combination where edge cases hide. This was worth doing on 1.0; on 1.1.0 it is mandatory, because "modified versions" gives no guarantee the original coverage survived.
Fixing is safe, with one caveat. phpcbf on docblocks does not change runtime behaviour unless tooling parses them reflectively (Doctrine annotations, some serializer fall-backs, route discovery). Check app/Entities and app/Models for annotation strings before bulk-fixing.
Cache can hide violations. --cache keys on mtime; restored CI caches and git operations can produce a stale green. Use --no-cache in CI, keep the cache local, rm -rf .phpcs-cache when results look impossible.
Suppress with intent. Prefer // phpcs:ignore <SniffCode> over excluding a directory; prefer --sniffs=<code> over --standard= when you want one check, so a future ruleset change upstream does not silently add rules to your build.
The extension point is still to fork it — but that calculus just got worse. On 1.0 the fallback was "copy a few dozen lines into MyOrg.Commenting.ShortTypes". On 1.1.0 you would be forking modified upstream sniffs, meaning you inherit the same PHPCS-internals churn the package itself is now fighting. If the package stalls, the cheaper move may be a small independent regex sniff of your own over the docblock token stream, rather than carrying a patched copy of Squiz.Commenting.*.
Watch the PHPCS distribution split. PHPCS has been published as both squizlabs/php_codesniffer and phpcsstandards/php_codesniffer, and a small package may declare a narrow constraint against one name. If composer require fights you or proposes a downgrade, read the resolver output rather than accepting it blindly.
How can I help you explore Laravel packages today?