Skip to content

feat(GH-1634): move LoggerService from zmscitizenapi to zmsslim#2467

Open
ThomasAFink wants to merge 21 commits into
mainfrom
feat-gh-1634-move-logger-service-to-zmsslim
Open

feat(GH-1634): move LoggerService from zmscitizenapi to zmsslim#2467
ThomasAFink wants to merge 21 commits into
mainfrom
feat-gh-1634-move-logger-service-to-zmsslim

Conversation

@ThomasAFink
Copy link
Copy Markdown
Member

@ThomasAFink ThomasAFink commented May 30, 2026

Relocate HTTP logging, rate limiting, and header filtering to BO\Slim\LoggerService with configurable enrichers for citizen-api process context and error codes.

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.
  • Ich habe erforderliche Dokumentation im Ordner docs hinzugefügt.

Summary by CodeRabbit

  • New Features

    • Per-module HTTP request logging with configurable rate limits, response capture and enriched context
    • Module initializer registers request logging, configurable security-header handling (with JSON error responses) and request sanitization
    • Improved client-IP detection and new PHP error handler
    • Environment-based header color theming in admin/statistic UIs
  • Configuration

    • Replaced global LOGGER_* vars with component-specific ZMS_LOGGER settings
    • New request-sanitization limits (max string length, recursion depth)
  • Tests

    • Expanded tests for client IP, logging, middleware, cache and error-handler behaviors

Review Change Stack

Relocate HTTP logging, rate limiting, and header filtering to BO\Slim\LoggerService
with configurable enrichers for citizen-api process context and error codes.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors logging into BO\Slim\LoggerService with cache-backed rate limiting and callback hooks, adds a ClientIp helper, introduces ModuleLoggerInitializer for per-module env-driven logger configuration and middleware registration, moves/adapts Slim middleware and tests, and updates module bootstraps, env templates, and docs.

Changes

Logger Service Refactoring and Core Infrastructure

Layer / File(s) Summary
ClientIp helper extraction
zmsslim/src/Slim/Helper/ClientIp.php, zmsslim/composer.json, zmsslim/tests/Slim/Helper/ClientIpTest.php
New BO\Slim\Helper\ClientIp::getClientIp() implements prioritized $_SERVER header parsing, validation, and fallback; tests added; composer requires PSR-16/symfony cache.
LoggerService refactor to BO\Slim
zmsslim/src/Slim/LoggerService.php
LoggerService moved to BO\Slim with static CacheInterface $cache, injectable $requestContextEnricher and $errorCodeResolver, cache-backed rate limiting with lock/retry/backoff, configurable stack/response sizing, and improved header/param redaction and request/error payload handling.
ClientIpHelper delegation
zmscitizenapi/src/Zmscitizenapi/Utils/ClientIpHelper.php
Delegates getClientIp() to BO\Slim\Helper\ClientIp::getClientIp().
Middleware relocation and API changes
zmsslim/src/Slim/Middleware/*, zmscitizenapi/src/.../Middleware/*
Middleware moved to BO\Slim\Middleware, imports updated to BO\Slim\LoggerService; RequestSanitizer constructor now accepts maxRecursionDepth and maxStringLength; SecurityHeadersMiddleware accepts configurable headers and an optional error-response builder.
Service imports updated
zmscitizenapi/src/.../ZmsApiClientService.php, ZmsApiFacadeService.php, Utils/ErrorHandler.php
Add/update use BO\Slim\LoggerService; so static logging calls resolve to new logger class.
Test infrastructure
zmsslim/tests/Slim/TestLogger.php, zmsslim/tests/Slim/Middleware/MiddlewareTestCase.php, zmsslim/tests/Slim/*
Add TestLogger extending BO\Slim\LoggerService, base MiddlewareTestCase, and migrate/extend LoggerService and middleware tests to the new static configuration and test helpers.

Module Logger Initialization and Wiring

Layer / File(s) Summary
ModuleLoggerInitializer
zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php
New helper reads ZMS_<MODULE>_LOGGER_* env vars, calls LoggerService::configure(), initializes filesystem PSR-16 cache (Symfony adapter wrapped with Psr16Cache) and assigns it to LoggerService::$cache, exposes request-sanitizer limits, and registers HTTP middleware (request logging, optional security headers, request sanitizer).
Bootstrap wiring and callbacks
zmscitizenapi/bootstrap.php
Bootstrap wires LoggerService::$requestContextEnricher = ProcessContextExtractor::extractProcessContext, sets $errorCodeResolver to map via ErrorMessages::get, provides a $securityHeaderErrorResponse callable for SecurityHeadersMiddleware, and passes request limits into RequestSanitizer.
Application logger & cache wiring
zmscitizenapi/src/Zmscitizenapi/Application.php, zmsapi/src/Zmsapi/Application.php
Applications read module-prefixed ZMS_*_LOGGER_* env vars, call \BO\Slim\LoggerService::configure(self::getLoggerConfig()), expose getRequestLimits(), and assign the application PSR-16 cache to LoggerService::$cache. zmsapi also registers logger-related middleware in bootstrap.

Per-Module Deployment and UI tweaks

Layer / File(s) Summary
Module bootstrap calls and Application initialize()
zmsadmin/bootstrap.php, zmscalldisplay/bootstrap.php, zmsstatistic/bootstrap.php, zmsticketprinter/bootstrap.php, corresponding Application.php files
Add ModuleLoggerInitializer::registerHttpMiddleware(false) in bootstraps; Applications add public static ?CacheInterface $cache and initialize() calling ModuleLoggerInitializer to configure logger and tryInitializeCache().
UI theming moved to classes
zmsadmin/templates/*, zmsstatistic/templates/*, SCSS files
Move env-based header coloring from inline styles to body-level classes; add SCSS rules for dev/stage header colors.
Env templates and docs
.ddev/.env.template, .devcontainer/.env.template, docs/en/operations/monolog-logging.md, docs/de/operations/monolog-logging.md, zmscitizenapi/README.md
Replace global LOGGER_* variables with per-module ZMS_<MODULE>_LOGGER_* variables; update documentation with per-module HTTP request logging and related code references.
Tests updated
zmscitizenapi/tests/*, zmsslim/tests/*
Tests updated to use prefixed env vars, adapted to ClientIp helper behavior (IpFilter test), and migrate to BO\Slim test namespaces and TestLogger expectations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

Module: zmsdldb

Suggested reviewers

  • Fabinatix97

"I twitch my whiskers, log lines bound to spring,
Per-module hops and cache-backed ring.
IPs discovered, headers kept discreet,
Middleware lined up, rate limits on repeat.
A rabbit cheers — the logs now sing!" 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.53% 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 PR title accurately summarizes the main change: relocating LoggerService from zmscitizenapi to zmsslim, which is the primary refactoring objective described in the PR details.
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 feat-gh-1634-move-logger-service-to-zmsslim

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.

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: 2

🧹 Nitpick comments (5)
zmsslim/tests/Slim/Helper/ClientIpTest.php (2)

24-33: ⚡ Quick win

Add a malformed-header regression case.

The suite only covers valid forwarded headers and the no-header fallback. Please add a case where a forwarded header is present but invalid and REMOTE_ADDR is valid, so the helper is forced to skip the bad value instead of returning it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/tests/Slim/Helper/ClientIpTest.php` around lines 24 - 33, Add a new
test method (e.g. testSkipsMalformedForwardedHeader) in Slim/Helper/ClientIpTest
that sets $_SERVER['HTTP_X_FORWARDED_FOR'] to a malformed value (for example
'not-an-ip' or 'unknown') and $_SERVER['REMOTE_ADDR'] to a valid IP like
'203.0.113.5', then assert that ClientIp::getClientIp() returns the REMOTE_ADDR
value; ensure the test cleans or overrides $_SERVER appropriately so the
malformed forwarded header is skipped and the helper falls back to using
REMOTE_ADDR.

12-15: ⚡ Quick win

Reset the full $_SERVER surface this helper reads.

ClientIp::getClientIp() inspects more headers than the two unset here. If another test sets HTTP_CLIENT_IP, HTTP_FORWARDED, etc., that value can leak into later cases and make this class order-dependent. Snapshot/restore $_SERVER or unset the full header list. As per coding guidelines, "Tests 4. Independent. 5. Repeatable."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/tests/Slim/Helper/ClientIpTest.php` around lines 12 - 15, The
tearDown currently only unsets HTTP_X_FORWARDED_FOR and REMOTE_ADDR but
ClientIp::getClientIp() reads many more server headers; fix by snapshotting
$_SERVER in setUp and restoring it in tearDown (or explicitly unset the full
header list ClientIp reads such as HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR,
HTTP_FORWARDED, HTTP_FORWARDED_FOR, HTTP_X_REAL_IP, REMOTE_ADDR, etc.) so tests
are independent and repeatable; modify the tearDown method and add a
corresponding setUp to save/restore $_SERVER or add the complete unset list
referenced above, keeping ClientIp::getClientIp() header names in sync.
zmsslim/tests/Slim/LoggerServiceTest.php (1)

159-190: ⚡ Quick win

Assert the header redaction result here.

This test now seeds X-Api-Key, but it still only checks that logRequest() does not throw. If header filtering regresses and logs the raw secret, this test will still pass. Please assert the emitted context or logger interaction so the redaction behavior is actually covered.

As per coding guidelines, **/*: Tests should be "Readable" and "Independent".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/tests/Slim/LoggerServiceTest.php` around lines 159 - 190, The test
currently only ensures LoggerService::logRequest doesn't throw but doesn't
verify header redaction; update testSensitiveHeaderFiltering to assert the
logger was called with redacted header values by mocking/instrumenting the
logger used by LoggerService and adding an expectation (or capturing the logged
context) that the headers in the emitted log do not contain the raw secret
'secret-key' (and do not expose the Authorization token) — e.g., set a mock on
the logger method (info/debug) and use a PHPUnit callback to assert the headers
array in the logged context replaces sensitive values (not equal to the original
secret) or includes the known redaction placeholder used by LoggerService; keep
references to LoggerService::logRequest and testSensitiveHeaderFiltering when
locating where to add the mock and assertions.
zmscitizenapi/src/Zmscitizenapi/Application.php (1)

14-15: 💤 Low value

TODO comments should reference tracked issues.

Lines 14, 79, and 120 contain TODO comments indicating planned refactors but lack issue references. As per coding guidelines, these should be converted to proper issues or reference existing ones.

Example format
// `@see` Issue `#XXX`: Refactor Application into smaller focused classes
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmscitizenapi/src/Zmscitizenapi/Application.php` around lines 14 - 15,
Replace the bare TODO comments in the Application class with referenced issue
tags: create or link tracked issues and update each TODO to the format shown in
the example (e.g. "// `@see` Issue `#XXX`: Refactor Application into smaller focused
classes") so every TODO in class Application (top-of-class TODO and the other
TODOs near the logger/middleware initializers) points to a specific issue
number; ensure the comment text briefly describes the intended refactor and uses
the `@see` Issue `#NNN` pattern for traceability.
zmsslim/composer.json (1)

26-27: 💤 Low value

Duplicate roave/security-advisories dependency.

roave/security-advisories is listed in both require (line 26) and require-dev (line 33). This meta-package only needs to be in require-dev since it prevents installing vulnerable packages during development/CI without affecting production runtime.

♻️ Suggested fix
         "tracy/tracy": "^2.9",
-        "roave/security-advisories": "dev-latest",
         "psr/simple-cache": "^3.0"
     },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/composer.json` around lines 26 - 27, Duplicate dependency
"roave/security-advisories" appears in composer.json under both "require" and
"require-dev"; remove the entry from "require" so the package remains only in
"require-dev" (leave the "psr/simple-cache" and other production deps untouched)
— locate the "require" block that contains "roave/security-advisories" and
delete that line, ensuring the "require-dev" block retains the
"roave/security-advisories" entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@zmsslim/src/Slim/Helper/ClientIp.php`:
- Around line 26-32: The code currently returns the first comma-split header
value even if invalid; update the logic in ClientIp.php so instead of returning
$ip unconditionally, iterate through $ips and validate each candidate using
filter_var(..., FILTER_VALIDATE_IP) and return the first valid one, otherwise
continue scanning other headers and only fall back to REMOTE_ADDR (or null) at
the end; ensure you change the block that uses $_SERVER[$header], $ips and $ip
so no unvalidated header value can be returned (this prevents bad HTTP_* headers
from overriding REMOTE_ADDR and affecting RateLimitingMiddleware).

In `@zmsslim/src/Slim/LoggerService.php`:
- Around line 213-215: The array_filter call that builds $queryParams in
LoggerService:: (use the $queryParams variable and the array_filter call with
ARRAY_FILTER_USE_BOTH) uses a callback whose parameters are named ($key, $value)
but ARRAY_FILTER_USE_BOTH passes (value, key), which is misleading; rename the
callback parameters to ($value, $key) and update any references inside the
closure accordingly (or swap their usage) so the parameter names match the
actual semantics of array_filter's callback with ARRAY_FILTER_USE_BOTH.

---

Nitpick comments:
In `@zmscitizenapi/src/Zmscitizenapi/Application.php`:
- Around line 14-15: Replace the bare TODO comments in the Application class
with referenced issue tags: create or link tracked issues and update each TODO
to the format shown in the example (e.g. "// `@see` Issue `#XXX`: Refactor
Application into smaller focused classes") so every TODO in class Application
(top-of-class TODO and the other TODOs near the logger/middleware initializers)
points to a specific issue number; ensure the comment text briefly describes the
intended refactor and uses the `@see` Issue `#NNN` pattern for traceability.

In `@zmsslim/composer.json`:
- Around line 26-27: Duplicate dependency "roave/security-advisories" appears in
composer.json under both "require" and "require-dev"; remove the entry from
"require" so the package remains only in "require-dev" (leave the
"psr/simple-cache" and other production deps untouched) — locate the "require"
block that contains "roave/security-advisories" and delete that line, ensuring
the "require-dev" block retains the "roave/security-advisories" entry.

In `@zmsslim/tests/Slim/Helper/ClientIpTest.php`:
- Around line 24-33: Add a new test method (e.g.
testSkipsMalformedForwardedHeader) in Slim/Helper/ClientIpTest that sets
$_SERVER['HTTP_X_FORWARDED_FOR'] to a malformed value (for example 'not-an-ip'
or 'unknown') and $_SERVER['REMOTE_ADDR'] to a valid IP like '203.0.113.5', then
assert that ClientIp::getClientIp() returns the REMOTE_ADDR value; ensure the
test cleans or overrides $_SERVER appropriately so the malformed forwarded
header is skipped and the helper falls back to using REMOTE_ADDR.
- Around line 12-15: The tearDown currently only unsets HTTP_X_FORWARDED_FOR and
REMOTE_ADDR but ClientIp::getClientIp() reads many more server headers; fix by
snapshotting $_SERVER in setUp and restoring it in tearDown (or explicitly unset
the full header list ClientIp reads such as HTTP_CLIENT_IP,
HTTP_X_FORWARDED_FOR, HTTP_FORWARDED, HTTP_FORWARDED_FOR, HTTP_X_REAL_IP,
REMOTE_ADDR, etc.) so tests are independent and repeatable; modify the tearDown
method and add a corresponding setUp to save/restore $_SERVER or add the
complete unset list referenced above, keeping ClientIp::getClientIp() header
names in sync.

In `@zmsslim/tests/Slim/LoggerServiceTest.php`:
- Around line 159-190: The test currently only ensures LoggerService::logRequest
doesn't throw but doesn't verify header redaction; update
testSensitiveHeaderFiltering to assert the logger was called with redacted
header values by mocking/instrumenting the logger used by LoggerService and
adding an expectation (or capturing the logged context) that the headers in the
emitted log do not contain the raw secret 'secret-key' (and do not expose the
Authorization token) — e.g., set a mock on the logger method (info/debug) and
use a PHPUnit callback to assert the headers array in the logged context
replaces sensitive values (not equal to the original secret) or includes the
known redaction placeholder used by LoggerService; keep references to
LoggerService::logRequest and testSensitiveHeaderFiltering when locating where
to add the mock and assertions.
🪄 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: deee8e53-299e-4b97-a000-10c5b305ae0c

📥 Commits

Reviewing files that changed from the base of the PR and between daf80c0 and 2ffbf37.

⛔ Files ignored due to path filters (1)
  • zmsslim/composer.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • zmscitizenapi/bootstrap.php
  • zmscitizenapi/src/Zmscitizenapi/Application.php
  • zmscitizenapi/src/Zmscitizenapi/Middleware/IpFilterMiddleware.php
  • zmscitizenapi/src/Zmscitizenapi/Middleware/RateLimitingMiddleware.php
  • zmscitizenapi/src/Zmscitizenapi/Middleware/RequestLoggingMiddleware.php
  • zmscitizenapi/src/Zmscitizenapi/Middleware/RequestSanitizerMiddleware.php
  • zmscitizenapi/src/Zmscitizenapi/Middleware/RequestSizeLimitMiddleware.php
  • zmscitizenapi/src/Zmscitizenapi/Middleware/SecurityHeadersMiddleware.php
  • zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiClientService.php
  • zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php
  • zmscitizenapi/src/Zmscitizenapi/Utils/ClientIpHelper.php
  • zmscitizenapi/src/Zmscitizenapi/Utils/ErrorHandler.php
  • zmscitizenapi/tests/Zmscitizenapi/TestLogger.php
  • zmsslim/composer.json
  • zmsslim/src/Slim/Helper/ClientIp.php
  • zmsslim/src/Slim/LoggerService.php
  • zmsslim/tests/Slim/Helper/ClientIpTest.php
  • zmsslim/tests/Slim/LoggerServiceTest.php

Comment thread zmsslim/src/Slim/Helper/ClientIp.php Outdated
Comment thread zmsslim/src/Slim/LoggerService.php Outdated
… logging

Extract request logging, sanitization, and security headers middleware into zmsslim so zmsapi can log every HTTP request via LoggerService alongside zmscitizenapi.
Match zmscitizenapi by annotating initializeLogger() until logger config is extracted into a dedicated initializer.
…rvice

Rename the ARRAY_FILTER_USE_BOTH callback parameters to ($value, $key) so they match PHP semantics.
Skip malformed comma-separated values and continue scanning headers so bad HTTP_* values cannot override REMOTE_ADDR.
…citizenapi

Split shared LOGGER_* settings into ZMS_CITIZENAPI_LOGGER_* and ZMS_API_LOGGER_* so each module can tune request logging independently, matching the per-module Twig cache pattern.
…and ticketprinter

Introduce ModuleLoggerInitializer in zmsslim and wire per-module ZMS_*_LOGGER_* settings with shared HTTP logging middleware across the remaining Slim web modules.
Explain ZMS_*_LOGGER_* env vars and recommend lower LOGGER_MAX_REQUESTS for zmscalldisplay and zmsticketprinter due to frequent monitor and printer polling.
…th ClientIp

ModuleLoggerInitializer needs symfony/cache as a direct zmsslim dependency so admin, calldisplay, statistic, and ticketprinter can bootstrap in CI. Update IpFilterMiddlewareTest after ClientIp skips invalid REMOTE_ADDR and falls back to 127.0.0.1.
SecurityHeadersMiddleware sets style-src 'self', which prevents browsers from applying the inline background-color on #page-header that Twig used for ZMS_ENV dev (green) and stage (blue). Move those colors to body.zms-env-* CSS classes and skip strict CSP on Twig UI modules that still rely on inline styles elsewhere; APIs keep SecurityHeadersMiddleware unchanged.
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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
zmsslim/src/Slim/Helper/ClientIp.php (1)

21-30: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Do not trust forwarded headers unless the sender is a trusted proxy.

Any direct client can currently spoof HTTP_X_FORWARDED_FOR/HTTP_CLIENT_IP and choose the IP that flows into logging and per-IP controls. This helper should only read forwarded headers when REMOTE_ADDR belongs to a configured trusted-proxy list; otherwise it should return the validated REMOTE_ADDR.

As per coding guidelines, "Always find root cause. Always look for the root cause of a problem."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/src/Slim/Helper/ClientIp.php` around lines 21 - 30, The code
currently trusts forwarded headers in the foreach ($headers as $header) loop
(e.g., HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP) without verifying the sender;
change the logic in the ClientIp helper so it first checks that
$_SERVER['REMOTE_ADDR'] is in a configured trusted-proxy list (or use a
getTrustedProxies() helper/config) and only then parses and returns forwarded
IPs, otherwise validate and return $_SERVER['REMOTE_ADDR'] via filter_var;
ensure the trusted-proxy check happens before the foreach and keep the existing
IP validation (filter_var with FILTER_VALIDATE_IP) when parsing header values.
🧹 Nitpick comments (5)
zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php (2)

17-19: 💤 Low value

Unjustified @SuppressWarnings(PHPMD.NPathComplexity) on a simple config builder.

configure() only assembles an array of env-backed defaults. The NPath inflation comes from the repeated ?: chain; suppressing the warning hides that rather than addressing it. Consider a tiny intFromEnv($key, $default) helper to drop both the complexity and the suppression.

As per coding guidelines: "Suppressing warnings without proper justification" / "Consider refactoring instead of suppressing".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php` around lines 17 - 19,
The `@SuppressWarnings` on NPathComplexity should be removed and the repeated ?:
chains in configure() refactored into a small helper; add a private static
intFromEnv(string $key, int $default): int (or a similarly named helper) that
reads getenv()/$_ENV, casts/validates to int and returns the default when
missing/invalid, then replace the multiple inline ?: expressions in configure()
with calls to intFromEnv() (and similarly factor any repeated string/default
patterns into a simple envFromDefault helper) to reduce NPath complexity and
eliminate the suppression.

51-57: 💤 Low value

Request limits read global env vars while logger config is per-module.

configure() keys off {$envPrefix}_LOGGER_*, but getRequestLimits() reads the unprefixed MAX_STRING_LENGTH/MAX_RECURSION_DEPTH, so all modules sharing an environment get identical sanitizer limits. If per-module tuning is the goal of this PR, consider prefixing these too; otherwise this is fine but worth a clarifying note.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php` around lines 51 - 57,
getRequestLimits() currently reads global env vars MAX_STRING_LENGTH and
MAX_RECURSION_DEPTH, which conflicts with the per-module logger configuration in
configure() that uses a module-specific $envPrefix and keys like
{$envPrefix}_LOGGER_*. Update getRequestLimits() to read prefixed variables
using the same $envPrefix pattern (e.g., "{$envPrefix}_MAX_STRING_LENGTH" and
"{$envPrefix}_MAX_RECURSION_DEPTH") or accept $envPrefix as a parameter so each
module can supply its prefix; ensure fallbacks to the existing defaults (32768
and 10) remain when the prefixed vars are unset.
zmsapi/src/Zmsapi/Application.php (1)

146-162: 💤 Low value

New @SuppressWarnings/@TODO indicates carried-over technical debt.

initializeLogger() is a flat sequence of env reads; the NPath suppression masks the ?: chain rather than fixing it, and the @TODO flags an unaddressed extraction. Note that ModuleLoggerInitializer::configure() already encapsulates exactly this logic — zmsapi could call it with prefix ZMS_API instead of duplicating the per-key reads and the suppression.

As per coding guidelines: flag "@SuppressWarnings ... without proper justification" and "TODO comments that indicate technical debt".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsapi/src/Zmsapi/Application.php` around lines 146 - 162, The
initializeLogger() method duplicates environment-key parsing and suppresses
NPath complexity; replace this duplication by calling the existing
ModuleLoggerInitializer::configure() with the prefix "ZMS_API" (or equivalent)
and then call LoggerService::configure(self::getLoggerConfig()) if still needed,
removing the `@SuppressWarnings` and `@TODO`; specifically remove the getenv(...)
casts and the static property assignments in initializeLogger(), invoke
ModuleLoggerInitializer::configure('ZMS_API') to centralize per-key reads, and
delete the TODO/NPath suppression comment so the duplication is resolved and
logging initialization is delegated.
zmsslim/tests/Slim/TestLogger.php (1)

60-72: ⚡ Quick win

logError only asserts the exception class, not the message; also missing the null-testCase guard.

assertInstanceOf(get_class($expected[1]), $exception) ignores the message, so a callsite like expectLogError(new \RuntimeException('Handler failed')) passes regardless of the actual error message — weakening the very tests relying on it. Additionally, unlike logInfo (Line 40), this method dereferences self::$testCase->fail(...) without a null check.

♻️ Strengthen assertion and align null handling
     public static function logError(
         \Throwable $exception,
         ?RequestInterface $request = null,
         ?ResponseInterface $response = null,
         array $context = []
     ): void {
+        if (self::$testCase === null) {
+            throw new \RuntimeException('Test case not initialized. Call initTest() first.');
+        }
+
         $expected = array_shift(self::$expectedLogs);
         if (!$expected) {
             self::$testCase->fail('Unexpected log error: ' . $exception->getMessage());
         }
         self::$testCase->assertEquals('error', $expected[0]);
         self::$testCase->assertInstanceOf(get_class($expected[1]), $exception);
+        self::$testCase->assertSame($expected[1]->getMessage(), $exception->getMessage());
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/tests/Slim/TestLogger.php` around lines 60 - 72, The logError method
currently only checks exception class and calls self::$testCase->fail() without
guarding for null; update logError to first shift $expected from
self::$expectedLogs, then if !$expected, check if self::$testCase is null and if
so throw or return (match behavior in logInfo) otherwise call
self::$testCase->fail(...); keep the existing class assertion
(assertInstanceOf(get_class($expected[1]), $exception)) but add an assertion
that the exception message equals the expected message (e.g.
self::$testCase->assertEquals($expected[1]->getMessage(),
$exception->getMessage())); ensure you reference the same symbols: logError,
self::$expectedLogs, self::$testCase, assertInstanceOf, and assertEquals when
implementing these checks.
zmscalldisplay/src/Zmscalldisplay/Application.php (1)

88-89: ⚖️ Poor tradeoff

Load-time initialize() couples class autoloading to filesystem cache setup.

ModuleLoggerInitializer::initializeCache() validates and creates a filesystem cache directory and can throw RuntimeException. Triggering it as a side effect when the class file is included means any cache-dir problem aborts class loading itself, which is harder to diagnose than a failure during explicit bootstrap. Consider invoking Application::initialize() from bootstrap.php (alongside the existing middleware registration) rather than at file load.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmscalldisplay/src/Zmscalldisplay/Application.php` around lines 88 - 89,
Application::initialize() is being called at file-load time which causes
ModuleLoggerInitializer::initializeCache() (which can throw RuntimeException) to
run during class inclusion; remove the immediate call to
Application::initialize() from the class file and instead invoke
Application::initialize() explicitly from bootstrap.php alongside the existing
middleware registration so cache setup happens during bootstrap rather than at
include time, ensuring failures surface during startup rather than class
autoloading.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@zmsadmin/src/Zmsadmin/Application.php`:
- Around line 116-123: Application::initialize currently calls
ModuleLoggerInitializer::initializeCache() unconditionally which lets
ModuleLoggerInitializer::validateCacheDirectory() throw and abort startup;
change Application::initialize to call
ModuleLoggerInitializer::initializeCache() inside a try/catch, preserve the call
to ModuleLoggerInitializer::configure('ZMS_ADMIN'), and on exception set
self::$cache = null (and optionally log a warning) so bootstrap continues and
LoggerService::checkRateLimit() can handle a null cache fallback.

In `@zmsslim/src/Slim/Middleware/RequestSanitizerMiddleware.php`:
- Around line 19-23: The constructor RequestSanitizerMiddleware::__construct
must validate that $maxRecursionDepth and $maxStringLength are positive; add
checks at the start of the constructor to throw an InvalidArgumentException
(with a clear message mentioning which parameter is invalid) if either value is
<= 0 so the middleware fails fast during bootstrap; keep assignments to
$this->maxRecursionDepth and $this->maxStringLength unchanged after the
validation.

In `@zmsslim/src/Slim/Middleware/SecurityHeadersMiddleware.php`:
- Around line 31-32: The callback stored in $errorResponseBuilder may return
null but process() (and the other spot at the second usage) currently returns
that value directly from the process() method which must return a
ResponseInterface; update both call sites (the uses of
$this->errorResponseBuilder in process()) to check the result and handle a null
return: if the builder returns null, either construct and return a default
ResponseInterface (e.g. a 500 error response) or rethrow/wrap the original
Throwable as a Http-friendly error so a ResponseInterface is always returned;
ensure you reference the property $errorResponseBuilder and the process() method
when making the change.

In `@zmsslim/tests/Slim/Helper/ModuleLoggerInitializerTest.php`:
- Around line 17-25: The tearDown in ModuleLoggerInitializerTest resets
LoggerService with an incomplete/incorrect config; update
ModuleLoggerInitializerTest::tearDown to call LoggerService::configure with the
full set of default keys including messageSize and backoffMax and ensure
stackLines is 20 and lockTimeout is 5 (matching the real defaults) so the static
LoggerService state is fully restored and tests don't leak configuration into
other suites.

In `@zmsslim/tests/Slim/TestLogger.php`:
- Around line 38-72: TestLogger::logError is missing the same self::$testCase
initialization guard as logInfo and only asserts the exception class; add the
init check (throw RuntimeException('Test case not initialized. Call initTest()
first.') if self::$testCase === null), ensure you obtain the expected entry from
self::$expectedLogs (use array_shift as in logInfo), assert expected[0] ===
'error', assert the exception class (assertInstanceOf) and also assert the
exception message contains the expected message (use assertStringContainsString
on $exception->getMessage() vs expected[1] when expected[1] is a string), and if
expected[2] exists iterate its keys to assertArrayHasKey/assertEquals against
the $context just like logInfo uses self::$expectedLogs, self::$testCase,
logInfo and logError to locate the changes.

---

Outside diff comments:
In `@zmsslim/src/Slim/Helper/ClientIp.php`:
- Around line 21-30: The code currently trusts forwarded headers in the foreach
($headers as $header) loop (e.g., HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP) without
verifying the sender; change the logic in the ClientIp helper so it first checks
that $_SERVER['REMOTE_ADDR'] is in a configured trusted-proxy list (or use a
getTrustedProxies() helper/config) and only then parses and returns forwarded
IPs, otherwise validate and return $_SERVER['REMOTE_ADDR'] via filter_var;
ensure the trusted-proxy check happens before the foreach and keep the existing
IP validation (filter_var with FILTER_VALIDATE_IP) when parsing header values.

---

Nitpick comments:
In `@zmsapi/src/Zmsapi/Application.php`:
- Around line 146-162: The initializeLogger() method duplicates environment-key
parsing and suppresses NPath complexity; replace this duplication by calling the
existing ModuleLoggerInitializer::configure() with the prefix "ZMS_API" (or
equivalent) and then call LoggerService::configure(self::getLoggerConfig()) if
still needed, removing the `@SuppressWarnings` and `@TODO`; specifically remove the
getenv(...) casts and the static property assignments in initializeLogger(),
invoke ModuleLoggerInitializer::configure('ZMS_API') to centralize per-key
reads, and delete the TODO/NPath suppression comment so the duplication is
resolved and logging initialization is delegated.

In `@zmscalldisplay/src/Zmscalldisplay/Application.php`:
- Around line 88-89: Application::initialize() is being called at file-load time
which causes ModuleLoggerInitializer::initializeCache() (which can throw
RuntimeException) to run during class inclusion; remove the immediate call to
Application::initialize() from the class file and instead invoke
Application::initialize() explicitly from bootstrap.php alongside the existing
middleware registration so cache setup happens during bootstrap rather than at
include time, ensuring failures surface during startup rather than class
autoloading.

In `@zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php`:
- Around line 17-19: The `@SuppressWarnings` on NPathComplexity should be removed
and the repeated ?: chains in configure() refactored into a small helper; add a
private static intFromEnv(string $key, int $default): int (or a similarly named
helper) that reads getenv()/$_ENV, casts/validates to int and returns the
default when missing/invalid, then replace the multiple inline ?: expressions in
configure() with calls to intFromEnv() (and similarly factor any repeated
string/default patterns into a simple envFromDefault helper) to reduce NPath
complexity and eliminate the suppression.
- Around line 51-57: getRequestLimits() currently reads global env vars
MAX_STRING_LENGTH and MAX_RECURSION_DEPTH, which conflicts with the per-module
logger configuration in configure() that uses a module-specific $envPrefix and
keys like {$envPrefix}_LOGGER_*. Update getRequestLimits() to read prefixed
variables using the same $envPrefix pattern (e.g.,
"{$envPrefix}_MAX_STRING_LENGTH" and "{$envPrefix}_MAX_RECURSION_DEPTH") or
accept $envPrefix as a parameter so each module can supply its prefix; ensure
fallbacks to the existing defaults (32768 and 10) remain when the prefixed vars
are unset.

In `@zmsslim/tests/Slim/TestLogger.php`:
- Around line 60-72: The logError method currently only checks exception class
and calls self::$testCase->fail() without guarding for null; update logError to
first shift $expected from self::$expectedLogs, then if !$expected, check if
self::$testCase is null and if so throw or return (match behavior in logInfo)
otherwise call self::$testCase->fail(...); keep the existing class assertion
(assertInstanceOf(get_class($expected[1]), $exception)) but add an assertion
that the exception message equals the expected message (e.g.
self::$testCase->assertEquals($expected[1]->getMessage(),
$exception->getMessage())); ensure you reference the same symbols: logError,
self::$expectedLogs, self::$testCase, assertInstanceOf, and assertEquals when
implementing these checks.
🪄 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: 007d0d27-22e7-453d-b1b8-6d4b3d57f5ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2ffbf37 and 72c24a5.

⛔ Files ignored due to path filters (5)
  • zmsadmin/composer.lock is excluded by !**/*.lock
  • zmscalldisplay/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 (42)
  • .ddev/.env.template
  • .devcontainer/.env.template
  • docs/de/operations/monolog-logging.md
  • docs/en/operations/monolog-logging.md
  • zmsadmin/bootstrap.php
  • zmsadmin/custom_scss/admin.scss
  • zmsadmin/scss/admin.scss
  • zmsadmin/src/Zmsadmin/Application.php
  • zmsadmin/templates/block/layout/main.twig
  • zmsadmin/templates/layout/fullwidth.twig
  • zmsadmin/templates/layout/main.twig
  • zmsapi/bootstrap.php
  • zmsapi/src/Zmsapi/Application.php
  • zmscalldisplay/bootstrap.php
  • zmscalldisplay/src/Zmscalldisplay/Application.php
  • zmscitizenapi/README.md
  • zmscitizenapi/bootstrap.php
  • zmscitizenapi/src/Zmscitizenapi/Application.php
  • zmscitizenapi/tests/Zmscitizenapi/ApplicationTest.php
  • zmscitizenapi/tests/Zmscitizenapi/Middleware/IpFilterMiddlewareTest.php
  • zmsslim/composer.json
  • zmsslim/src/Slim/Helper/ClientIp.php
  • zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php
  • zmsslim/src/Slim/LoggerService.php
  • zmsslim/src/Slim/Middleware/RequestLoggingMiddleware.php
  • zmsslim/src/Slim/Middleware/RequestSanitizerMiddleware.php
  • zmsslim/src/Slim/Middleware/SecurityHeadersMiddleware.php
  • zmsslim/tests/Slim/Helper/ClientIpTest.php
  • zmsslim/tests/Slim/Helper/ModuleLoggerInitializerTest.php
  • zmsslim/tests/Slim/Middleware/MiddlewareTestCase.php
  • zmsslim/tests/Slim/Middleware/RequestLoggingMiddlewareTest.php
  • zmsslim/tests/Slim/Middleware/RequestSanitizerMiddlewareTest.php
  • zmsslim/tests/Slim/Middleware/SecurityHeadersMiddlewareTest.php
  • zmsslim/tests/Slim/TestLogger.php
  • zmsstatistic/bootstrap.php
  • zmsstatistic/custom_scss/statistic.scss
  • zmsstatistic/scss/statistic.scss
  • zmsstatistic/src/Zmsstatistic/Application.php
  • zmsstatistic/templates/layout/fullwidth.twig
  • zmsstatistic/templates/layout/main.twig
  • zmsticketprinter/bootstrap.php
  • zmsticketprinter/src/Zmsticketprinter/Application.php
✅ Files skipped from review due to trivial changes (5)
  • zmscitizenapi/README.md
  • zmsadmin/scss/admin.scss
  • zmsadmin/bootstrap.php
  • docs/en/operations/monolog-logging.md
  • docs/de/operations/monolog-logging.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • zmsslim/composer.json
  • zmsslim/src/Slim/LoggerService.php

Comment thread zmsadmin/src/Zmsadmin/Application.php
Comment thread zmsslim/src/Slim/Middleware/RequestSanitizerMiddleware.php
Comment thread zmsslim/src/Slim/Middleware/SecurityHeadersMiddleware.php
Comment thread zmsslim/tests/Slim/Helper/ModuleLoggerInitializerTest.php
Comment thread zmsslim/tests/Slim/TestLogger.php
Use tryInitializeCache() so Twig modules keep running when CACHE_DIR is missing or unwritable; validate sanitizer limits at construction; rethrow when SecurityHeadersMiddleware error builder returns null; tighten TestLogger::logError assertions and test teardown.
Register PhpErrorHandler from Bootstrap::configureLogger() so engine-level
warnings no longer appear as unstructured "NOTICE: PHP message: PHP Warning"
lines on stderr; they are logged through App::$log with file, line, and errno.
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: 1

🧹 Nitpick comments (1)
zmsslim/tests/Slim/Helper/PhpErrorHandlerTest.php (1)

52-56: ⚡ Quick win

Restore error_reporting() in a finally block.

If PhpErrorHandler::handle() ever throws here, the process stays in error_reporting(0) and later tests become misleading.

♻️ Proposed fix
-        $previousReporting = error_reporting(0);
-
-        $handled = PhpErrorHandler::handle(E_USER_WARNING, 'suppressed warning', __FILE__, __LINE__);
-
-        error_reporting($previousReporting);
+        $previousReporting = error_reporting(0);
+        try {
+            $handled = PhpErrorHandler::handle(E_USER_WARNING, 'suppressed warning', __FILE__, __LINE__);
+        } finally {
+            error_reporting($previousReporting);
+        }
As per coding guidelines, tests should be “Independent” and “Repeatable”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@zmsslim/tests/Slim/Helper/PhpErrorHandlerTest.php` around lines 52 - 56, Wrap
the call to PhpErrorHandler::handle(...) with a try/finally so
error_reporting($previousReporting) always runs; specifically, capture
$previousReporting = error_reporting(0), then call
PhpErrorHandler::handle(E_USER_WARNING, 'suppressed warning', __FILE__,
__LINE__) inside a try block and restore error_reporting($previousReporting) in
the finally block to guarantee restoration even if handle() throws.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@zmsslim/src/Slim/Helper/PhpErrorHandler.php`:
- Around line 17-33: The handler in PhpErrorHandler::handle currently always
returns true after logging, which suppresses PHP's built-in handling for
E_USER_ERROR; modify handle(int $severity, string $message, string $file, int
$line) so that after logging it returns false when $severity === E_USER_ERROR
(or E_ERROR-equivalent user-level constant) to allow PHP's default error handler
to run, otherwise keep the existing behavior (return true for non-fatal errors);
locate the severityToLogLevel call and logging block to add the conditional
return based on the $severity.

---

Nitpick comments:
In `@zmsslim/tests/Slim/Helper/PhpErrorHandlerTest.php`:
- Around line 52-56: Wrap the call to PhpErrorHandler::handle(...) with a
try/finally so error_reporting($previousReporting) always runs; specifically,
capture $previousReporting = error_reporting(0), then call
PhpErrorHandler::handle(E_USER_WARNING, 'suppressed warning', __FILE__,
__LINE__) inside a try block and restore error_reporting($previousReporting) in
the finally block to guarantee restoration even if handle() throws.
🪄 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: a89ab936-c429-4b73-9db1-7701f1b6f8ff

📥 Commits

Reviewing files that changed from the base of the PR and between 72c24a5 and eccdf14.

📒 Files selected for processing (14)
  • zmsadmin/src/Zmsadmin/Application.php
  • zmscalldisplay/src/Zmscalldisplay/Application.php
  • zmsslim/src/Slim/Bootstrap.php
  • zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php
  • zmsslim/src/Slim/Helper/PhpErrorHandler.php
  • zmsslim/src/Slim/Middleware/RequestSanitizerMiddleware.php
  • zmsslim/src/Slim/Middleware/SecurityHeadersMiddleware.php
  • zmsslim/tests/Slim/Helper/ModuleLoggerInitializerTest.php
  • zmsslim/tests/Slim/Helper/PhpErrorHandlerTest.php
  • zmsslim/tests/Slim/Middleware/RequestSanitizerMiddlewareTest.php
  • zmsslim/tests/Slim/Middleware/SecurityHeadersMiddlewareTest.php
  • zmsslim/tests/Slim/TestLogger.php
  • zmsstatistic/src/Zmsstatistic/Application.php
  • zmsticketprinter/src/Zmsticketprinter/Application.php
🚧 Files skipped from review as they are similar to previous changes (5)
  • zmsticketprinter/src/Zmsticketprinter/Application.php
  • zmsstatistic/src/Zmsstatistic/Application.php
  • zmsslim/src/Slim/Helper/ModuleLoggerInitializer.php
  • zmsslim/tests/Slim/TestLogger.php
  • zmsadmin/src/Zmsadmin/Application.php

Comment thread zmsslim/src/Slim/Helper/PhpErrorHandler.php
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
PhpErrorHandler must preserve the previous error-to-exception behavior so
missing route parameters still fail fast instead of reaching domain logic.
RequestLoggingMiddleware consumed the PSR-7 response stream while logging,
so internal HTTP clients such as zmscalldisplay received empty API bodies.
Throwing after logging turned every warning into a fatal request failure
(empty API bodies, calldisplay 500s). Log warnings as structured JSON and
return true so execution continues as before PHP error handler registration.
…havior

Module test bootstraps convert PHP notices into ErrorException before
asserting domain exceptions. Do not replace that handler when PHPUnit runs;
production still logs warnings as JSON without aborting requests.
Ticketprinter and other modules pass nested query params (e.g. statusList[0]).
Casting those values to string triggered "Array to string conversion", which
PhpErrorHandler logged and (on older builds) escalated to a fatal ErrorException.
Extract path building and error parsing from logRequest so PHPMD stays under the complexity threshold without changing logging behavior.
Regenerate composer.lock for zmsadmin, zmscalldisplay, zmsslim,
zmsstatistic, and zmsticketprinter from merged composer.json files.
Regenerate locks so Symfony 8.1 and doctrine/instantiator 2.1 (PHP 8.4+) are replaced with PHP 8.3-compatible versions.
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.

Move LoggerService in zmscitizenapi to zmsslim

1 participant