Skip to content

Conversation

@ttsuru
Copy link
Contributor

@ttsuru ttsuru commented Oct 30, 2025

PHPStan Level 5 Compliance - Complete Type Safety

Achieved 100% PHPStan Level 5 compliance with comprehensive type declarations across the entire CakePHP 2.x codebase.

Achievement

PHPStan Level 5: 599 errors → 0 errors

All type safety issues have been resolved through systematic addition of type declarations, fixing type coercion issues, and resolving contravariance problems.

Comprehensive Type Declarations

Model Layer (265+ errors resolved)

  • TreeBehavior: 265 errors - Complete method signatures
  • Model, I18nModel: All methods with return types
  • Behaviors: TranslateBehavior, ContainableBehavior, AclBehavior
  • BehaviorCollection, ModelBehavior: Full type coverage
  • AclNode, Permission: Complete type declarations

DataSource Layer

  • DboSource: All methods with complete type signatures
  • Database Drivers: Mysql, Postgres, Sqlite, Sqlserver
  • DataSource, CakeSession, ConnectionManager: Full coverage
  • Removed unsafe extract() usage in buildColumn()
  • Fixed truncate() return type: PDOStatement|bool|null

Controller Layer

  • Controller, Component: Base class type declarations
  • Auth: All authenticators and authorizers (Base, Basic, Digest, Form, Actions, Controller, Crud)
  • Components: Acl, Security, RequestHandler, Flash, Cookie, Session
  • Acl Refactoring: Split PhpAcl into PhpAco and PhpAro classes
  • Scaffold: Complete type declarations

Console Layer (46+ errors resolved)

  • Shell, ShellDispatcher: Complete signatures
  • Commands: AclShell (46 errors), SchemaShell (25 errors), ApiShell (13 errors), I18nShell, TestShell, ConsoleShell
  • Tasks: ExtractTask (22 errors), CommandTask
  • Helpers: TableShellHelper
  • ConsoleOutput, ConsoleInput: Full type coverage

Core & Utilities

  • Configure: Type declarations + bug fix
  • App, CakeObject: Complete coverage
  • I18n, L10n, Multibyte: All methods typed
  • CakeEventManager, CakeEvent: Full coverage
  • CakeText, Folder: Type declarations
  • LegacyClassLoader: Changed autoload() to : void (spl_autoload_register requirement)

Error & Exception Layer

  • All exception classes: Added $previous parameter support
  • All exception classes: Type declarations for constructor parameters
  • Property type declarations for message templates

Test Infrastructure

  • CakeTestFixture: Updated signatures
  • CakeBaseReporter, CakeHtmlReporter, CakeTextReporter: Type declarations
  • Replaced SqlserverTestResultIterator with proper PDOStatement mocks
  • All test mocks updated to return correct types

Global Functions (functions.php)

  • debug(), h(), pluginSplit(), sortByKey(): Complete type declarations
  • All global helper functions now fully typed

Code Quality Improvements

Multibyte Optimization

Simplified ASCII character conversion - removed unnecessary loops:

// Before (6 lines, inefficient)
$str = strtolower(chr($char));
$strlen = strlen($str);
for ($ii = 0; $ii < $strlen; $ii++) {
    $lower = ord(substr($str, $ii, 1));
}
$lowerCase[] = $lower;

// After (1 line, clear intent)
$lower = ord(strtolower(chr($char)));
$lowerCase[] = $lower;

Applied to both strtolower() and strtoupper() methods.

EmailConfig Interface

  • New EmailConfigInterface for type-safe email configuration
  • Updated UPGRADE.md with migration guide
  • Ensures consistent email configuration across applications

Test Mock Improvements

  • All database transaction mocks return proper boolean values
  • Prevents type coercion issues with typed return values
  • Improved test reliability

CI/CD Enhancements

  • Added cs2pr integration for PHPStan results in Pull Requests
  • PHPStan errors now display as inline PR annotations
  • Improved developer experience for code review

Breaking Changes

EmailConfig Interface Requirement

All email configuration classes must now implement EmailConfigInterface:

// Before
class EmailConfig {
    public $default = [...];
}

// After
use Cake\Network\Email\EmailConfigInterface;

class EmailConfig implements EmailConfigInterface {
    public $default = [...];
}

See UPGRADE.md for detailed migration instructions.

Statistics

  • Total Commits: 23
  • Files Changed: 300+
  • PHPStan Errors Resolved: 599
  • Lines Changed: ~2000+ additions, ~1500+ deletions
  • Test Coverage: All tests passing
  • Code Style: PHPCS compliant

Key Learnings

Type Coercion Issues

Discovered that adding : bool return type to methods causes null to be converted to false:

public function commit(): bool {
    // Mock returns null by default
    // With : bool, null becomes false
    // Solution: Add ->willReturn(true) to mocks
}

Autoloader Return Types

PHP's spl_autoload_register() requires autoloaders to return void, not bool. The return value is ignored by PHP - it doesn't control whether subsequent autoloaders are called.

Array Type Checking

The pattern $keys === array_values($keys) correctly checks if array keys are sequential from 0, though it may appear redundant at first glance.

Related

  • UPGRADE.md: Updated with EmailConfigInterface migration guide
  • CHANGELOG.md: Documented all changes and bug fixes
  • phpstan.neon: Level 5 configuration

This PR represents a major milestone in code quality and type safety for CakePHP 2.x, bringing it closer to modern PHP standards while maintaining backward compatibility.

Add type declarations to 233 files including parameter types, return types, and property types. Upgrade PHPStan from level 0 to 5. All tests passing but 755 PHPStan errors remain (work in progress).
Change PHPStan error format from checkstyle to github for better integration with GitHub Actions annotations. Remove intermediate checkstyle.xml file and cs2pr step.
@codecov
Copy link

codecov bot commented Oct 30, 2025

ttsuru added 21 commits October 30, 2025 09:52
Update type declaration to accept null for the plain parameter to match actual usage patterns in the codebase.
Run phpcbf to automatically fix code style issues.
Update PHPStan step to output checkstyle format and use cs2pr to
display results as annotations in pull requests for better visibility.
Document the requirement for EmailConfig classes to implement
EmailConfigInterface, including before/after examples and migration steps.
… type

Update the find() method signature to match Model::find() and fix the
custom 'popular' type handling to use conditions instead of overwriting
the type parameter.
- Update Model::find() signature: string|null $type, array|null $query
- Fix Model property types: useTable, displayField (string|bool|null)
- Fix I18nModel property type declarations
- Update CakeEmail to use EmailConfigInterface
- Fix ModelValidator to handle null data properly
- Add type declarations to SessionHandler interfaces
- Fix View class property types
- Update all test models to match new Model::find() signature
- Fix custom find methods in test models
- Add proper type hints to fixture classes
- Fix Shell and ConsoleShell type issues
- Update PaginatorComponent parameter types
- Clean up EmailConfigInterface formatting
Add comprehensive type declarations across datasource and test classes:
- Add bool return types to DboSource transaction methods (begin, commit, rollback)
- Fix DboSource::defaultConditions() to accept bool type for conditions parameter
- Add type declarations to DboSource property types (affected, numRows, took)
- Remove extract() usage in buildColumn() for better type safety
- Fix mock expectations in ModelWriteTest to return proper boolean values
- Add type declarations to DataSource and database driver classes
- Update test classes with proper type declarations for properties
- Fix queryAssociation resultSet parameter to accept array|false type

These changes improve type safety and resolve type-related issues discovered
during PHPStan Level 5 analysis, particularly around transaction handling
and null/bool type coercion in return values.
Add comprehensive type declarations to model behavior classes:
- TreeBehavior: Add return types and parameter types to all methods
- TranslateBehavior: Add type declarations for behavior methods
- ContainableBehavior: Add type declarations
- AclBehavior: Add type declarations and fix method signatures
- ModelBehavior: Add type declarations to base class

Update related test files to match new type signatures and fix
type-related test issues in behavior tests.

This significantly reduces PHPStan Level 5 errors, particularly
in TreeBehavior which had 265 errors.
Update truncate() method return type from bool|null to PDOStatement|bool|null
to match the actual return type of execute() method in DboSource and its
subclasses (Postgres, Sqlite, Sqlserver).

This fixes PHPStan type mismatch errors where truncate() calls execute()
which can return PDOStatement|bool|null.
Remove custom SqlserverTestResultIterator class and use PHPUnit's
PDOStatement mock instead in testDescribe(). This ensures proper
type compatibility with methods expecting PDOStatement return types.

The mock uses willReturnCallback to iterate through test data,
maintaining the same behavior as the original ArrayIterator-based
implementation while satisfying type requirements for _execute()
method which must return PDOStatement|bool.
- Remove unused ArrayIterator import from SqlserverTest
- Remove extra blank line in SqlserverTest
- Add blank line before return in SqlserverTest
- Fix spacing after type hint in Sqlite.php
- Add trailing comma in BehaviorCollectionTest
- Add trailing comma in ModelValidationTest
- Fix function declaration brace placement in DboSource
- Remove unused $keys variable in DboSource::conditionKeysToString()
Add type declarations to Console classes for PHPStan Level 5:
- Shell: Add return types and parameter types to shell methods
- AclShell: Add type declarations and fix method signatures
- ApiShell: Add type declarations
- CommandListShell: Add type declarations
- CompletionShell: Add type declarations
- ConsoleOutput: Add type declarations
- CakeText: Add type declarations

Update ShellTest to match new type signatures.

This reduces PHPStan Level 5 errors in console-related classes.
Add comprehensive type declarations for PHPStan Level 5:
- ConsoleShell: Add return types and parameter types
- I18nShell: Add type declarations
- SchemaShell: Add type declarations (25 errors reduced)
- ExtractTask: Add type declarations (22 errors reduced)
- TestShell: Add type declarations
- CakeObject: Add type declarations to base object class
- Folder: Add type declarations for file system operations

These changes significantly reduce PHPStan Level 5 errors in console
commands and core utility classes.
Add type declarations to CakeSchema class and update related tests:
- CakeSchema: Add return types and parameter types to all methods
- SchemaShell: Remove unused import
- Add i18n schema file for testing
- Update SchemaShellTest and CakeSchemaTest to match new signatures
- Update EmailComponentTest schema references
- Update CI workflow PHPStan configuration

This reduces PHPStan Level 5 errors in schema-related classes.
…lasses

Add type declarations for PHPStan Level 5 compliance:
- Database drivers: Add type declarations to Mysql, Postgres, Sqlite, Sqlserver
- Behaviors: Update ContainableBehavior, TranslateBehavior, AclBehavior
- Model: Add type declarations to Model, AclNode, Permission
- DataSource: Add type declarations to DataSource and CakeSession
- BehaviorCollection: Add type declarations
- ConnectionManager: Add type declarations
- L10n: Add type declarations (11 errors reduced)
- DboSource: Additional type refinements

Update phpstan.neon configuration and related tests to match new signatures.

This is a major step toward PHPStan Level 5 compliance, addressing type
safety across the Model layer and database abstraction.
Add comprehensive type declarations for PHPStan Level 5:
- Controller: Add type declarations to base Controller class
- Component: Add type declarations to base Component class
- ComponentCollection: Add type declarations
- Acl components: Add type declarations to DbAcl, IniAcl, PhpAcl
- Extract PhpAco and PhpAro classes from PhpAcl for better organization
- Auth components: Add type declarations to all authenticators and authorizers
  - BaseAuthenticate, BasicAuthenticate, DigestAuthenticate, FormAuthenticate
  - BaseAuthorize, ActionsAuthorize, ControllerAuthorize, CrudAuthorize
- AuthComponent, AclComponent: Add type declarations
- FlashComponent, RequestHandlerComponent, SecurityComponent: Add type declarations
- Scaffold: Add type declarations
- CakeErrorController: Add type declarations
- CakeRequest: Add type declarations

Update AuthComponentTest to match new signatures.

This significantly reduces PHPStan Level 5 errors in the Controller layer.
…sion

Critical bug fix and optimizations:
- Fix Configure::restore() writing true instead of cached values
- Simplify Multibyte::strtolower() and strtoupper() ASCII handling
- Remove unnecessary loops for single-byte character conversion
- Change LegacyClassLoader::autoload() return type to void
- Add type declarations to I18n, Configure, App, CakeEventManager
- Add type declarations to ConfigReaderInterface, IniReader, PhpReader
- Add type declarations to ShellDispatcher and ConsoleLog
- Remove unused Exception import from PrivateActionException

The Configure::restore() bug would cause all cached configuration
to be replaced with boolean true, breaking the cache restore feature.

All PHPStan Level 5 errors are now resolved.
Document PHPStan Level 5 compliance milestone in CHANGELOG.md:
- All 599 PHPStan Level 5 errors resolved
- Comprehensive type declarations across all layers
- Critical bug fixes including Configure::restore()
- Complete type coverage for Model, DataSource, Controller, Console layers

This represents a major milestone in type safety and code quality
for CakePHP 2.x.
@ttsuru ttsuru changed the title refactor: Add PHP 8+ type declarations for PHPStan Level 5 (WIP) refactor: Add PHP 8+ type declarations for PHPStan Level 5 Nov 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants