Skip to content

feat(ZMSKVR): implement php code scanning with psalm#2203

Merged
ThomasAFink merged 38 commits into
mainfrom
feature-zmskvr-implement-php-code-scanning-with-psalm
May 31, 2026
Merged

feat(ZMSKVR): implement php code scanning with psalm#2203
ThomasAFink merged 38 commits into
mainfrom
feature-zmskvr-implement-php-code-scanning-with-psalm

Conversation

@ThomasAFink
Copy link
Copy Markdown
Member

@ThomasAFink ThomasAFink commented Apr 15, 2026

Pull Request Checklist (Feature Branch to next):

  • Ich habe die neuesten Änderungen aus dem next Branch in meinen Feature-Branch gemergt.
  • Relevante Tests wurden mit zmsautomation ausgeführt.
  • Das Code-Review wurde abgeschlossen.
  • Fachliche Tests wurden durchgeführt und sind abgeschlossen.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling for communication failures with enhanced exception mapping.
  • Infrastructure

    • MariaDB upgraded from 10.4 → 10.11.
    • Added project-wide static analysis and security scan workflows and configurations.
    • Updated local development environment URL configuration.
  • Other Updates

    • Updated test utilities to reflect renamed entities.
    • Tightened class visibility in utility components.

ThomasAFink and others added 29 commits April 14, 2026 09:49
Ensure the Psalm workflow uploads SARIF findings even when Psalm returns a non-zero exit code, then fail the job explicitly afterward so CI enforcement remains intact.
Set upload-sarif checkout_path to the matrix project directory so GitHub can resolve Psalm paths and show code previews for alerts.
Switch upload-sarif checkout_path to the matrix project relative path so GitHub can resolve Psalm file locations correctly.
Rewrite SARIF artifact URIs to repository-root-relative paths so GitHub code scanning can resolve files and render alert previews.
Add Psalm as a dev dependency in each module, create per-module psalm.xml configs, and expand the Psalm CI matrix to scan all modules with module-aware SARIF path normalization.
Upload SARIF only when the module report exists and fail with a clear message when Psalm or SARIF generation fails.
Prevent matrix runs from canceling sibling jobs and commit regenerated composer.lock files after adding Psalm to all modules.
Disable dead-code checks in per-module scans to reduce alert noise and add an opt-in monorepo dead-code job that analyzes cross-module usage.
Execute the psalm-dead-code job alongside the matrix scan for push, pull request, scheduled, and manual workflow runs.
Add config/bootstrap/routing files to module and monorepo Psalm project files so global App symbols are available during analysis.
Match CodeQL Advanced on/pull_request and push to main, add pull-requests read permission, and cancel in-progress runs per ref.
…rror

Handle BO\Zmsclient\Psr7\RequestException (e.g. SSL/cURL failures) so
responses use zmsClientCommunicationError instead of unknownError.
Align ErrorMessages::get call with single-parameter signature.
PHP cURL to https://*.ddev.site from the web container often fails TLS
verification; same Apache is reachable over http://127.0.0.1 without SSL.
Remove root vendor ignore from psalm.monorepo.xml; with resolveFromConfigFile it pointed at missing ./vendor and broke CI.
Satisfy Psalm ClassMustBeFinal; class is not extended and only exposes static helpers.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

Review Change Stack

Warning

Review limit reached

@ThomasAFink, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 17 minutes and 10 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 065a32a6-9959-41c5-b834-889c12d625d0

📥 Commits

Reviewing files that changed from the base of the PR and between cce4bab and 4e05839.

⛔ Files ignored due to path filters (13)
  • mellon/composer.lock is excluded by !**/*.lock
  • zmsadmin/composer.lock is excluded by !**/*.lock
  • zmsapi/composer.lock is excluded by !**/*.lock
  • zmscalldisplay/composer.lock is excluded by !**/*.lock
  • zmscitizenapi/composer.lock is excluded by !**/*.lock
  • zmsclient/composer.lock is excluded by !**/*.lock
  • zmsdb/composer.lock is excluded by !**/*.lock
  • zmsdldb/composer.lock is excluded by !**/*.lock
  • zmsentities/composer.lock is excluded by !**/*.lock
  • zmsmessaging/composer.lock is excluded by !**/*.lock
  • zmsslim/composer.lock is excluded by !**/*.lock
  • zmsstatistic/composer.lock is excluded by !**/*.lock
  • zmsticketprinter/composer.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .github/workflows/psalm.yml
  • mellon/composer.json
  • psalm.monorepo.xml
  • zmsadmin/composer.json
  • zmsapi/composer.json
  • zmscalldisplay/composer.json
  • zmscitizenapi/composer.json
  • zmsclient/composer.json
  • zmsdb/composer.json
  • zmsdldb/composer.json
  • zmsentities/composer.json
  • zmsmessaging/composer.json
  • zmsslim/composer.json
  • zmsstatistic/composer.json
  • zmsticketprinter/composer.json
📝 Walkthrough

Walkthrough

Updates local DDEV settings (API URL, MariaDB version), amends CodeQL workflow display name, and adds a per-module vimeo/psalm dev dependency.

Changes

Monorepo infra & dev config

Layer / File(s) Summary
Local DDEV configuration
.ddev/.env.template, .ddev/config.yaml
ZMS_API_URL changed to http://127.0.0.1/terminvereinbarung/api/2; database.version updated from 10.4 to 10.11.
CI workflow metadata
.github/workflows/codeql.yml
CodeQL workflow name updated to include additional emoji characters (☕ 📜 🐍).
Psalm dev dependency addition (single module)
zmsadmin/composer.json
Added vimeo/psalm (^6.16.1) to require-dev for the zmsadmin module.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • it-at-m/eappointment#2201: Implements similar Psalm CI infrastructure and per-module Psalm configurations across the monorepo.

Suggested reviewers

  • Fabinatix97
  • MoDaae

Poem

"I hop through code with tiny paws,
Updated env and version laws.
A coffee, scroll, a psalm to run,
CI name shines like morning sun.
🐇✨"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: adding PHP code scanning with Psalm across multiple modules in the ZMSKVR project, which aligns with the primary objective of implementing Psalm static analysis.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature-zmskvr-implement-php-code-scanning-with-psalm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php Fixed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (3)
zmsapi/psalm.xml (1)

1-20: Consider reducing duplicated Psalm config across modules.

These per-module files are nearly identical; a shared template/base generation approach would reduce config drift and maintenance overhead.

As per coding guidelines "Be consistent. If you do something a certain way, do all similar things in the same way."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@zmsapi/psalm.xml` around lines 1 - 20, The per-module Psalm XML is
duplicated; extract the common settings (the <psalm> root attributes like
errorLevel, resolveFromConfigFile, xmlns/xsi,
findUnusedBaselineEntry/findUnusedCode and the <projectFiles> structure) into a
single shared base template and have module-level configs include or override
only module-specific entries (e.g., additional <file> or <directory> entries).
Implement this by creating a shared psalm base (template or generated file) and
update module psalm.xml to reference or merge the base, leaving only unique
<projectFiles> additions in each module; ensure attributes such as
findUnusedBaselineEntry="true" and the <ignoreFiles> vendor entry are defined
centrally so maintenance is centralized.
.github/workflows/psalm.yml (1)

170-181: Monorepo scan only installs dependencies for zmsapi.

The dead-code scan analyzes all 13+ projects but only installs composer dependencies in zmsapi. Psalm won't be able to resolve classes that depend on other projects' vendor packages, potentially causing false positives or analysis failures.

Consider installing dependencies for all projects, or at minimum documenting this limitation if intentional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/psalm.yml around lines 170 - 181, The workflow currently
only installs Composer deps for the zmsapi folder before running the "Run Psalm
dead-code scan (monorepo)" step (id: psalm_dead_code), which can cause
unresolved symbols when Psalm analyzes other packages; update the job to install
dependencies for every PHP project Psalm will scan (e.g., run composer install
in each package directory or run a root-level install that bootstraps all
workspaces) or explicitly document this limitation in the step description so
reviewers know the scan is intentionally limited.
zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php (1)

29-31: Consider tracking the TODO as a proper issue.

The TODO comment suggests refactoring to a strategy pattern but lacks a tracking reference. As per coding guidelines, TODO comments should be converted to proper issues:

// `@see` Issue `#XXX`: Refactor ExceptionService to use strategy pattern

This would help ensure the technical debt is tracked and prioritized appropriately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php` around
lines 29 - 31, The TODO in ExceptionService (class ExceptionService) should be
converted into a tracked issue reference: replace or augment the comment near
the class/method declaration (where `@SuppressWarnings` and the TODO appear) with
a link to a project issue (e.g., “@see Issue `#XXX`: Refactor ExceptionService to
use strategy pattern”) so the suggested refactor is tracked; update the comment
to reference the issue ID and keep the existing note about considering a
strategy pattern or error handler chain for the method(s) with high complexity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.ddev/.env.template:
- Line 2: The .ddev/.env.template change to ZMS_API_URL from
https://zms.ddev.site/... to http://127.0.0.1/... requires confirming team
agreement and updating all references; update the ZMS_API_URL value
documentation and tests that still reference zms.ddev.site by editing the
fixtures and docs listed
(zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_process_ics_template.json,
zmsapi/public/doc/README.md, zmscitizenapi/public/doc/README.md,
zmsdb/tests/Zmsdb/fixtures/mysql_zmsbo.sql) to use the new localhost URL, add a
note in the project README or deployment docs about using 127.0.0.1 for local
dev, and notify the team so everyone configures their local environment
accordingly.

In @.ddev/config.yaml:
- Line 12: Update the MariaDB version declaration and verify config
compatibility: inspect the "version: \"10.11\"" entry and any MariaDB
configuration files referenced by DDEV for deprecated or changed options (e.g.,
explicit_defaults_for_timestamp, InnoDB compression, slow query log variable
names), adjust settings to match 10.11 defaults where needed, run your Flyway
migration tests against a MariaDB 10.11 instance to confirm SQL compatibility,
and after upgrading run mariadb-upgrade to update system tables; ensure any
adjusted settings are committed back into the ddev config or custom my.cnf used
by the project.

In @.github/workflows/psalm.yml:
- Around line 88-115: The inline Python SARIF normalization script should skip
processing when the SARIF file is missing to avoid FileNotFoundError: check
existence of the computed sarif_path (variable sarif_path derived from PROJECT)
using os.path.exists(sarif_path) before the first open(sarif_path, ...) call and
exit or return early if the file is absent; ensure the script logs or prints a
short message and does not raise if the file doesn't exist so the workflow can
continue to the "Check SARIF file exists" step.
- Around line 126-131: The Upload SARIF results step using
github/codeql-action/upload-sarif@v3 is passing an incorrect checkout_path;
remove the checkout_path: ${{ matrix.project }} line from that step so the
action uses the default github.workspace and the SARIF file paths (which your
Python normalization already prefixes) are interpreted correctly.

In `@psalm.monorepo.xml`:
- Around line 11-48: Add an <ignoreFiles> section to psalm.monorepo.xml to
exclude each project's vendor directory from analysis; inside the root
(alongside <projectFiles>) add ignore entries referencing the vendor paths for
the projects listed (e.g., mellon/vendor, zmsadmin/vendor, zmsapi/vendor,
zmscalldisplay/vendor, zmscitizenapi/vendor, zmsclient/vendor, zmsdb/vendor,
zmsdldb/vendor, zmsentities/vendor, zmsmessaging/vendor, zmsslim/vendor,
zmsstatistic/vendor, zmsticketprinter/vendor) so Psalm won’t scan third-party
code and generate false positives or slow runs.

In `@zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php`:
- Around line 150-156: The switch contains duplicate assignments for the same
error causing an unused assignment and misleading fall-through; replace the two
separate case labels 'BO\\Zmsapi\\Exception\\Request\\RequestNotFound' and
'BO\\Zmsdb\\Exception\\Request\\RequestNotFound' with a single combined case
that calls self::getError('requestNotFound') once (i.e., group both case labels
together before one $error = self::getError('requestNotFound') and the break),
so update the switch in ExceptionService where getError('requestNotFound') is
invoked to remove the redundant assignment.

---

Nitpick comments:
In @.github/workflows/psalm.yml:
- Around line 170-181: The workflow currently only installs Composer deps for
the zmsapi folder before running the "Run Psalm dead-code scan (monorepo)" step
(id: psalm_dead_code), which can cause unresolved symbols when Psalm analyzes
other packages; update the job to install dependencies for every PHP project
Psalm will scan (e.g., run composer install in each package directory or run a
root-level install that bootstraps all workspaces) or explicitly document this
limitation in the step description so reviewers know the scan is intentionally
limited.

In `@zmsapi/psalm.xml`:
- Around line 1-20: The per-module Psalm XML is duplicated; extract the common
settings (the <psalm> root attributes like errorLevel, resolveFromConfigFile,
xmlns/xsi, findUnusedBaselineEntry/findUnusedCode and the <projectFiles>
structure) into a single shared base template and have module-level configs
include or override only module-specific entries (e.g., additional <file> or
<directory> entries). Implement this by creating a shared psalm base (template
or generated file) and update module psalm.xml to reference or merge the base,
leaving only unique <projectFiles> additions in each module; ensure attributes
such as findUnusedBaselineEntry="true" and the <ignoreFiles> vendor entry are
defined centrally so maintenance is centralized.

In `@zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php`:
- Around line 29-31: The TODO in ExceptionService (class ExceptionService)
should be converted into a tracked issue reference: replace or augment the
comment near the class/method declaration (where `@SuppressWarnings` and the TODO
appear) with a link to a project issue (e.g., “@see Issue `#XXX`: Refactor
ExceptionService to use strategy pattern”) so the suggested refactor is tracked;
update the comment to reference the issue ID and keep the existing note about
considering a strategy pattern or error handler chain for the method(s) with
high complexity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 83862cf4-3bbc-4e72-94b3-c55249245b4f

📥 Commits

Reviewing files that changed from the base of the PR and between ecdf4a4 and 561326b.

⛔ Files ignored due to path filters (13)
  • mellon/composer.lock is excluded by !**/*.lock
  • zmsadmin/composer.lock is excluded by !**/*.lock
  • zmsapi/composer.lock is excluded by !**/*.lock
  • zmscalldisplay/composer.lock is excluded by !**/*.lock
  • zmscitizenapi/composer.lock is excluded by !**/*.lock
  • zmsclient/composer.lock is excluded by !**/*.lock
  • zmsdb/composer.lock is excluded by !**/*.lock
  • zmsdldb/composer.lock is excluded by !**/*.lock
  • zmsentities/composer.lock is excluded by !**/*.lock
  • zmsmessaging/composer.lock is excluded by !**/*.lock
  • zmsslim/composer.lock is excluded by !**/*.lock
  • zmsstatistic/composer.lock is excluded by !**/*.lock
  • zmsticketprinter/composer.lock is excluded by !**/*.lock
📒 Files selected for processing (34)
  • .ddev/.env.template
  • .ddev/config.yaml
  • .github/workflows/codeql.yml
  • .github/workflows/psalm.yml
  • mellon/composer.json
  • mellon/psalm.xml
  • psalm.monorepo.xml
  • zmsadmin/composer.json
  • zmsadmin/psalm.xml
  • zmsapi/composer.json
  • zmsapi/psalm.xml
  • zmscalldisplay/composer.json
  • zmscalldisplay/psalm.xml
  • zmscitizenapi/composer.json
  • zmscitizenapi/psalm.xml
  • zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php
  • zmsclient/composer.json
  • zmsclient/psalm.xml
  • zmsdb/composer.json
  • zmsdb/psalm.xml
  • zmsdldb/composer.json
  • zmsdldb/psalm.xml
  • zmsdldb/src/Zmsdldb/Importer/test_sqlite.php
  • zmsentities/composer.json
  • zmsentities/psalm.xml
  • zmsmessaging/composer.json
  • zmsmessaging/psalm.xml
  • zmsslim/composer.json
  • zmsslim/psalm.xml
  • zmsslim/src/Slim/Helper/Sanitizer.php
  • zmsstatistic/composer.json
  • zmsstatistic/psalm.xml
  • zmsticketprinter/composer.json
  • zmsticketprinter/psalm.xml

Comment thread .ddev/.env.template
Comment thread .ddev/config.yaml
Comment thread .github/workflows/psalm.yml
Comment thread .github/workflows/psalm.yml
Comment thread psalm.monorepo.xml
Comment thread zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php
ThomasAFink and others added 9 commits May 8, 2026 17:18
…ith-psalm

# Conflicts:
#	zmsadmin/composer.lock
#	zmsapi/composer.lock
#	zmscalldisplay/composer.lock
#	zmscitizenapi/composer.lock
#	zmscitizenapi/src/Zmscitizenapi/Services/Core/ExceptionService.php
#	zmsclient/composer.lock
#	zmsdb/composer.lock
#	zmsdldb/composer.lock
#	zmsentities/composer.lock
#	zmsmessaging/composer.lock
#	zmsslim/composer.lock
#	zmsstatistic/composer.lock
#	zmsticketprinter/composer.lock
Psalm exits non-zero when it reports issues but still writes SARIF for
CodeQL upload. Treat missing SARIF as the job failure condition instead.
Use continue-on-error on the scan step so SARIF normalize/upload run even
when Psalm exits non-zero, then fail the job if SARIF is missing or the
recorded Psalm exit code is not zero.
Drop pull_request and push triggers so branch CI is not blocked by existing
findings; keep daily 05:00 UTC uploads on main with fail-on-error unchanged.
Psalm 6.16+ requires patched PHP 8.3.16+, which breaks reference-libraries
on GitHub runners; 6.5.0 is the last release accepting PHP 8.3.6.
Prevent the dead-code job from analyzing third-party dependencies across
all 13 modules, matching per-module psalm.xml ignoreFiles behavior.
@ThomasAFink ThomasAFink merged commit d0899b5 into main May 31, 2026
41 checks passed
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.

2 participants