Skip to content

bug: Replace PHP 8.0 match() with switch so Syslog pages parse under PHP 7.4 - #444

Merged
TheWitness merged 4 commits into
developfrom
fix/php74-match-parse
Nov 11, 2026
Merged

TheWitness merged 4 commits into
developfrom
fix/php74-match-parse

Conversation

@TheWitness

Copy link
Copy Markdown
Member

Description

Two shipping files used PHP 8.0 match() expressions:

  • includes/functions.php (syslog_rule_preview()) — loaded on every Syslog web page
  • syslog_removal.php (syslog_action_edit())

match is PHP 8.0 syntax, so it cannot be polyfilled. On a web server running PHP 7.4 — which the declared compat = 1.2.23 floor permits — includes/functions.php fails to compile. The page then returns a blank HTTP 503, logged by Cacti as:

CMDPHP WARNING: Cacti Page:syslog.php?headercontent=true for User:XXX Generated a Fatal Error:503

with no accompanying PHP error line (the classic signature of a parse/compile error). The CLI poller keeps working because it runs under a newer CLI PHP — a common split where PHP-FPM/mod_php is older than the CLI binary.

This replaces both simple string-keyed match() expressions with behavior-identical switch statements so the plugin parses under PHP 7.4 again. Unlike match, functions such as str_contains()/str_starts_with() are safe because Cacti core autoloads symfony/polyfill-php80.

Related Issue

Reported on the Cacti forums (4.2 → 4.11 upgrade, Cacti 1.2.30): https://forums.cacti.net/viewtopic.php?p=298770#p298770 — the final "Generated a Fatal Error:503" on the Syslog tab. Follow-up to #439 (array primary keys and process-helper polyfills).

Motivation and Context

The plugin advertises compat = 1.2.23, and those Cacti installations commonly run PHP 7.4. The CI matrix only tests PHP 8.2–8.6, so match() was never flagged. This restores the implied PHP 7.4 parse-safety for the full supported range.

How Has This Been Tested?

  • php -l includes/functions.php and php -l syslog_removal.php — no syntax errors (PHP 8.3.36).
  • Standalone equivalence harness comparing the old match() mapping to the new switch across every input (facility, host, program, unknown, empty, null, mixed case) — all outputs identical.
  • Confirmed these were the only two match() sites in the shipping tree, and that no other unpolyfillable PHP 8.0+ syntax (enums, readonly, nullsafe ?->, constructor promotion, named args, typed class constants) is present.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation repository accordingly.

includes/functions.php and syslog_removal.php used match() expressions
(PHP 8.0 syntax). includes/functions.php is loaded on every Syslog web
page, so on a web server running PHP 7.4 -- which the declared Cacti
1.2.23 compatibility floor permits -- the file fails to compile and the
page returns a blank HTTP 503 ("Generated a Fatal Error:503" logged by
Cacti help.php), while the CLI poller on a newer PHP keeps working.

match() is syntax and cannot be polyfilled, unlike str_contains() and
friends which Cacti core covers via symfony/polyfill-php80. Rewrite both
simple string-keyed match() expressions as behavior-identical switch
statements (every input, including the default, maps the same) so the
plugin parses on PHP 7.4 again.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 11, 2026 22:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

PHP 8-only union types remain in includes/functions.php, causing PHP 7.4 parsing to fail before any Syslog page can render.

1 open issue
What changed in this PR

This PR replaces two PHP 8-only match expressions with switch statements to address Syslog page parsing failures on PHP 7.4, following the compatibility work in #439. Remaining union-type declarations still prevent includes/functions.php from parsing on PHP 7.4.

Changes:

  • Replace column-selection expressions in rule previews and the removal-rule editor.
  • Add a changelog entry describing the intended compatibility fix.
File Description
syslog_removal.php Uses switch to select columns for legacy removal rules.
includes/​functions.php Uses switch to select incoming-field mappings for rule previews.
CHANGELOG.md Documents the intended PHP 7.4 compatibility restoration.

Review effort: Balanced

Comment thread includes/functions.php Resolved
…se safety

Replacing the two match() expressions alone did not make the Syslog pages
parse on PHP 7.4: includes/functions.php -- loaded on every page -- and
syslog.php, syslog_reports.php and syslog_removal.php still declared PHP
8.0 union types (string|false, array|false, int|string, string|int|float|
bool, ...) and mixed parameter/return types. PHP 7.4 cannot compile those
declarations, so the file still failed to load and the page still returned
a blank HTTP 503 (Generated a Fatal Error:503), while the CLI poller on a
newer PHP kept working. Reported by the PR review.

Drop the PHP 8.0 union and mixed type declarations from the affected
function signatures while keeping the documented @param/@return types in
the PHPDoc blocks. No behavior change: the removed declarations only
relaxed runtime type enforcement on values the bodies already handle.

Validated with PHPCompatibility (dev-develop, which understands PHP 8.x
syntax) at testVersion 7.4-: no union-type, mixed-type or false-return
declarations remain in the shipping tree. The only residual 7.4 notices
are str_starts_with()/str_contains() (provided on 7.4 by Cacti core's
bundled symfony/polyfill-php80) and a PHP 8.6 trim() default-value note,
neither of which is a parse error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bmfmancini
bmfmancini previously approved these changes Nov 11, 2026
The CSV import/export hardening regression asserted the literal signature
"function syslog_csv_safe(mixed $value): mixed" and extracted the function
body with a regex that required the ": mixed" return type. The PHP 7.4
compatibility backport (previous commit) removed that PHP 8.0 type
declaration, so the assertion failed CI ("Missing import/export
hardening").

Relax both checks to the type-agnostic signature "function
syslog_csv_safe($value)" and an optional-return-type regex. The test's
purpose -- verifying the CSV formula-injection defusing and the import
size limit -- is unchanged; it no longer pins a specific PHP type
declaration that the plugin's 1.2.23/PHP 7.4 compatibility floor forbids.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… covered

The PHP 7.4 backport replaced two match() expressions with switch
statements, but a switch only executes the arm matching the tested input,
so the untested arms (includes/functions.php host/program branches)
counted as uncovered changed lines and failed the 100% patch-coverage
gate.

Replace both switches with array lookups
($map[$key] ?? $default). The lookup table is built in full on every call,
so all mapped lines execute whenever the function runs and the changed
lines are covered by the existing syslog_rule_preview coverage. The
preceding defaults foreach guarantees the incoming-field keys are set, and
the removal map values are literals, so "?? default" is exactly equivalent
to the old match()/switch default arm. Still PHP 7.4 parse-safe.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

@TheWitness
TheWitness merged commit 9b3a9e5 into develop Nov 11, 2026
13 of 14 checks passed
@TheWitness
TheWitness deleted the fix/php74-match-parse branch November 11, 2026 23:22
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.

3 participants