Skip to content

Add PHPStan level 8 static analysis#156

Closed
JanTvrdik wants to merge 2 commits intomasterfrom
phpstan-level-8
Closed

Add PHPStan level 8 static analysis#156
JanTvrdik wants to merge 2 commits intomasterfrom
phpstan-level-8

Conversation

@JanTvrdik
Copy link
Copy Markdown
Member

Summary

  • Adds PHPStan 2.x at level 8 with a baseline for multi-version bridge compatibility errors
  • Fixes real type issues in driver code, configuration, and controllers
  • Adds a PHPStan CI job running on PHP 8.4

Inline fixes

  • BaseDriver/PgSqlDriver: Changed $tableNameQuoted/$schemaQuoted from null|string to string with '' default
  • MySqlDriver/PgSqlDriver: Cast preg_replace return to (string) in getInitTableSource()
  • BaseDriver: Initialized $queryLength before inner loop to satisfy flow analysis
  • DefaultConfiguration: Fixed lazy-init property types to list<Group>|null / array<string, IExtensionHandler>|null
  • StructureDiffGenerator: Added false checks for file_get_contents() and preg_split()
  • BaseController: Changed $mode type to Engine\Runner::MODE_*
  • CreateCommand: Added null guard for $foundYear out-parameter

Baseline (50 errors)

Bridge code uses method_exists() checks to support multiple library versions. PHPStan can only see the installed version, so it flags calls to methods from other versions as errors. These are baselined:

  • Doctrine DBAL (fetchAll(), exec() removed in 4.x) and ORM (getUpdateSchemaSql() signature change)
  • Nextras DBAL (convertToSql, convertBoolToSql, TYPE_* constants from older versions)
  • Nette Database (literal-string param, getRowCount() nullability)
  • Nette DI (ServiceDefinition vs Definitions\ServiceDefinition, Statement constructor, etc.)
  • Symfony Config (TreeBuilder constructor change)
  • Dibi (dibi class name casing, query() return type)
  • HttpController (goto flow analysis limitation)

Test plan

  • vendor/bin/phpstan analyse --no-progress reports 0 errors
  • tests/run-unit.sh passes (26 tests)
  • CI phpstan job passes on PHP 8.4

- Add phpstan/phpstan ^2.1 to require-dev
- Configure PHPStan at level 8 with baseline for bridge compatibility errors
- Fix real type issues: nullable driver properties, preg_replace returns,
  file_get_contents/preg_split false checks, lazy-init property types,
  Runner::MODE_* type annotation, and undefined variable in SQL parser
- Baseline 50 unfixable errors from multi-version bridge code (Doctrine,
  Nette, Symfony, Nextras DBAL, Dibi)
- Add PHPStan CI job on PHP 8.4
Copilot AI review requested due to automatic review settings February 25, 2026 19:31
PHPStan 2.x requires PHP 8.1+ but the project supports PHP >=7.1,
so having it in require-dev breaks composer install on older PHP versions.
Install it separately in the PHPStan CI job instead.
Copy link
Copy Markdown

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

This PR introduces PHPStan 2.x static analysis at level 8 (with a baseline for multi-version bridge compatibility) and makes several small type-safety fixes across drivers, configuration, and bridge/controller code. It also adds a dedicated CI job to run PHPStan on PHP 8.4.

Changes:

  • Add PHPStan configuration (level 8) plus a baseline to accommodate multi-version bridge code.
  • Fix/clarify types and flow for PHPStan in drivers, configuration, controllers, and bridges.
  • Add a GitHub Actions QA job to run phpstan analyse on PHP 8.4.

Reviewed changes

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

Show a summary per file
File Description
src/Drivers/PgSqlDriver.php Tighten $schemaQuoted to string and cast preg_replace() to string for PHPStan.
src/Drivers/MySqlDriver.php Cast preg_replace() to string for PHPStan.
src/Drivers/BaseDriver.php Tighten $tableNameQuoted to string and initialize $queryLength for flow analysis.
src/Controllers/BaseController.php Refine $mode phpDoc to Engine\Runner::MODE_*.
src/Configurations/DefaultConfiguration.php Fix lazy-init property phpDoc types to include null.
src/Bridges/SymfonyConsole/CreateCommand.php Add additional null guard around by-ref out param usage.
src/Bridges/DoctrineOrm/StructureDiffGenerator.php Handle file_get_contents()/preg_split() failure cases more safely.
phpstan.neon Add PHPStan config including baseline and analysis level/paths.
phpstan-baseline.neon Baseline existing compatibility/static-analysis issues in bridge code.
composer.json Add phpstan/phpstan to require-dev.
.github/workflows/qa.yaml Add a PHPStan CI job running on PHP 8.4.

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

$name = $this->getFileName($label, $extension);

if ($this->hasNumericSubdirectory($dir, $foundYear)) {
if ($this->hasNumericSubdirectory($dir, $foundYear) && $foundYear !== null) {
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

$foundYear is used as an out-parameter without being initialized in this scope. In PHP this can lead to an "undefined variable" notice (and it also forces extra null checks for static analysis). Initialize the variable (e.g. to null) before passing it by reference, and then the additional $foundYear !== null guard becomes unnecessary.

Suggested change
if ($this->hasNumericSubdirectory($dir, $foundYear) && $foundYear !== null) {
$foundYear = null;
$foundMonth = null;
if ($this->hasNumericSubdirectory($dir, $foundYear)) {

Copilot uses AI. Check for mistakes.
Comment on lines 77 to +80
$content = file_get_contents($this->ignoredQueriesFile);
if ($content === false) {
return [];
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

file_get_contents() will emit a PHP warning on failure (missing/unreadable file) even though you handle the false return value. If the intent is to treat an unreadable ignored-queries file as "no ignored queries", consider suppressing the warning (as done elsewhere in the codebase) or checking readability/existence before reading to avoid noisy output.

Copilot uses AI. Check for mistakes.
@JanTvrdik JanTvrdik closed this Feb 25, 2026
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