Skip to content

Bump PHPStan to level 8 and fix all errors#1027

Draft
jamisonbryant wants to merge 10 commits into5.nextfrom
fix/phpstan-and-command-props
Draft

Bump PHPStan to level 8 and fix all errors#1027
jamisonbryant wants to merge 10 commits into5.nextfrom
fix/phpstan-and-command-props

Conversation

@jamisonbryant
Copy link
Contributor

Summary

  • Bumps PHPStan analysis level from 7 to 8
  • Makes $io and $args properties non-nullable in BakeSimpleMigrationCommand (they are always set before use in bake())
  • Fixes all 52 PHPStan level 8 errors across the codebase

Changes by category

Command classes

  • BakeSimpleMigrationCommand: Made $io/$args non-nullable — these properties are always assigned in bake() before any method reads them
  • BakeMigrationDiffCommand: Added null guards for getColumn()/getConstraint() nullable returns

Db/Table value objects

  • Column::getNull(): Coalesce nullable ?bool property to false (matches SQL default)
  • ForeignKey::getOnDelete()/getOnUpdate(): Guard null from getDelete()/getUpdate() before calling mapAction()

Db adapters (Mysql, Postgres, Sqlite, Sqlserver, Abstract, TimedOutput)

  • Cast preg_replace results to (string) where hardcoded patterns can't fail
  • Coalesce Constraint::getColumns() nullable return with ?? []
  • Cast ForeignKey::getReferencedTable() and Column::getName() at call sites
  • Assert non-null in AbstractAdapter::getConnection() after lazy init
  • Use null-safe ?-> for optional ConsoleIo in TimedOutputAdapter

Other

  • Db/Plan/Plan.php, Db/Table.php: Same getColumns()/getName() patterns
  • BaseSeed.php: Guard nullable getConfig() offset access
  • Util/ColumnParser.php: Cast preg_replace result
  • Util/TableFinder.php: Null-check association before method call
  • View/Helper/MigrationHelper.php: Use isset() for constraint type check

Test plan

  • tools/phpstan analyse --memory-limit=-1 passes with 0 errors at level 8
  • Existing test suite still passes

Jamison Bryant added 6 commits February 24, 2026 10:44
Column::getNull() now coalesces the nullable bool property to
false. ForeignKey::getOnDelete()/getOnUpdate() guard against null
from getDelete()/getUpdate() before calling mapAction().
Cast preg_replace results to string, coalesce nullable getColumns()
returns, cast nullable getReferencedTable()/getName() at call sites,
and use null-safe operator for optional ConsoleIo.
Guard nullable config access in BaseSeed, cast preg_replace in
ColumnParser, null-check association in TableFinder, and use
isset for constraint type in MigrationHelper.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Raises static analysis strictness by bumping PHPStan from level 7 to 8 and updates the codebase to satisfy the stricter type expectations (primarily around nullability and string/array shapes) across migrations commands, DB adapters, and helpers.

Changes:

  • Bump PHPStan level to 8.
  • Add null-safety and type coercions across schema diffing, adapters, and helpers to eliminate level-8 findings.
  • Tighten some command properties’ nullability and guard config access in seeding.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/View/Helper/MigrationHelper.php Adds isset() guard for constraint type access.
src/Util/TableFinder.php Avoids potential null association access when iterating associations.
src/Util/ColumnParser.php Casts preg_replace() result to string for PHPStan.
src/Db/Table/ForeignKey.php Guards nullable getDelete()/getUpdate() before mapping actions.
src/Db/Table/Column.php Coalesces nullable null flag to boolean default.
src/Db/Table.php Adds casts around nullable column type/name usage and PK filtering.
src/Db/Plan/Plan.php Coalesces nullable FK columns to [] when remapping conflicts.
src/Db/Adapter/TimedOutputAdapter.php Uses nullsafe operator for optional ConsoleIo.
src/Db/Adapter/SqlserverAdapter.php Casts regex results to string and coalesces nullable FK columns/table.
src/Db/Adapter/SqliteAdapter.php Casts regex/regex-callback results to string; coalesces nullable FK columns/table.
src/Db/Adapter/PostgresAdapter.php Casts regex results to string; coalesces nullable FK columns and update columns.
src/Db/Adapter/MysqlAdapter.php Casts potentially nullable column name and referenced table.
src/Db/Adapter/AbstractAdapter.php Adds assertion for non-null connection after lazy init; coalesces FK columns.
src/Command/BakeSimpleMigrationCommand.php Makes $io / $args non-nullable properties.
src/Command/BakeMigrationDiffCommand.php Adds null guards around nullable column/constraint lookups.
src/BaseSeed.php Guards nullable config access when building ManagerFactory options.
phpstan.neon Bumps analysis level from 7 to 8.
Comments suppressed due to low confidence (1)

src/Util/ColumnParser.php:217

  • Casting preg_replace() to string changes the failure mode when PCRE errors occur: a null result becomes '', which can lead to generating an empty/incorrect referenced table name. Instead of casting, handle a null return explicitly (e.g., fall back to the original $fieldName or abort with a clear exception).
                // Remove common suffixes like _id and pluralize
                $referencedTable = (string)preg_replace('/_id$/', '', $fieldName);
                $referencedTable = Inflector::pluralize($referencedTable);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ADmad ADmad changed the base branch from 5.x to 5.next February 24, 2026 19:20
@jamisonbryant jamisonbryant added this to the 5.next milestone Feb 24, 2026
Jamison Bryant added 4 commits February 24, 2026 15:36
Add missing null checks for getConstraint() in the "same name
constraints" loop in BakeMigrationDiffCommand to match the guards
already present in the "new" and "removed" loops. Replace assert()
with an explicit InvalidArgumentException in Table::addColumn() so
null type is caught in all environments, not just when assertions
are enabled. Add test coverage for the null type case.
Assertions can be disabled in production, which would allow a null
connection to slip through silently. Throw a RuntimeException instead
to fail fast in all environments.
Replace (string) casts with assign-and-check pattern for optional
values that can legitimately be null: Index::getWhere(),
ForeignKey::getName(), Index::getName(), Column::getGenerated().
This avoids silently converting null to empty string.
…le()

Replace (string) casts with explicit null checks and
InvalidArgumentException throws for values that must be present:
Column::getName() in SQL generation and ForeignKey::getReferencedTable()
in FK definition builders. A column without a name or a foreign key
without a referenced table are always programming errors that should
fail fast rather than silently produce broken SQL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants