Repository navigation
chore: PHPStan level 8 typing pass + bugfixes - #30
Merged
Merged
Conversation
Brings plugin_wmi to PHPStan level 8 with 0 errors (35 -> 0, after
adding missing Cacti-core function stubs).
Real bugs fixed:
- functions.php (plugin_wmi_create_resource_xml()): the exported WMI
query Resource XML referenced a nonexistent script_path
(scripts/wmic-script.php) and script_function (wmic_script) - both
typo'd with an extra "c". Confirmed against this plugin's own shipped
example resource XML (templates/resource/script_server/exchange_messages.xml),
which correctly uses scripts/wmi-script.php / wmi_script. Anyone who
exported and re-imported this query's resource file would get a
script server error, since the referenced script/function don't exist.
- wmi_accounts.php / wmi_queries.php (actions_accounts()/actions_queries()):
$account_actions[get_request_var('drp_action')] / $ds_actions[...] were
indexed by a value only validated as *some* integer, not restricted to
the single real action id (1). Added an early
array_key_exists()-checked reject-and-redirect guard before the lookup
(same bug shape fixed in plugin_tags/plugin_apcupsd earlier in this
rollout).
- poller_wmi.php (plugin_wmi_version() caller / display_version()):
hardened plugin_wmi_version()'s parse_ini_file() result and added a
guard in its caller before dereferencing $info['version'] (same
parse_ini_file hardening pattern established in plugin_apcupsd/plugin_cycle).
- Several `db_fetch_row_prepared()` results (functions.php, poller_wmi.php,
setup.php, wmi_accounts.php, wmi_queries.php) were read via array offset
without narrowing out the `bool` half of their `array|bool` return type -
added `is_array($x) ? $x : []` narrowing at each site.
- linux_wmi.php: `explode()` on a public, mutable `$separator` property
with no non-empty guard (would throw a PHP 8 ValueError if ever set to
'' at runtime); `unserialize()` called on `base64_decode()`'s result
without checking for `false` first; `proc_open()`'s command array
wrapped in `array_values()` to guarantee a list type.
- Several `html_start_box()` call sites passing `'', '3'` instead of
`false, 3` for the `$div`/`$cell_padding` params (recurring pattern
across this whole rollout).
- poller_wmi.php: `process_device($host_id)` cast to int at its call site
(CLI args are always strings; the function's own docblock declares
`@param int $host_id`).
Also ran php-cs-fixer in real fix mode (only docblock-alignment diffs)
and regenerated locales/po/cacti.pot (reverted per-language .po/.mo
churn, per this rollout's established Weblate-ownership convention).
Full Pest suite: 34 passed (270 assertions), unchanged before/after.
TheWitness
requested review from
bmfmancini and
xmacan
and
a lite review from Copilot
September 25, 2026 13:44
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
An empty separator can still cause transport output and response parsing to use different delimiters, producing malformed query data.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
What changed in this PR
This PR completes a PHPStan level 8 typing pass and fixes WMI export, validation, and runtime-safety issues.
Changes:
- Hardened database, configuration, decoding, and process handling.
- Corrected exported WMI script references and bulk-action validation.
- Updated documentation, changelog, and translation references.
| File | Summary |
|---|---|
wmi_tools.php |
UI argument and result-handling fixes |
wmi_script.php |
PHPDoc return-type updates |
wmi_queries.php |
Action validation and database-result narrowing |
wmi_accounts.php |
Action validation and database-result narrowing |
setup.php |
Plugin setup and result-handling hardening |
script/wmi-script.php |
PHPDoc updates |
poller_wmi.php |
Version handling and host ID typing |
locales/po/cacti.pot |
Refreshed translation references |
linux_wmi.php |
Transport, parsing, and decoding safeguards; empty-separator handling remains inconsistent |
functions.php |
Resource XML corrections and result handling |
CHANGELOG.md |
Documented fixes |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…or both the transport request and result parsing Addresses Copilot review feedback: the earlier fallback only guarded explode() against an empty separator when parsing results, but still passed the raw (possibly empty) ->separator to the transport via Wmi_Request, so an empty separator would make the transport emit unsplit rows while the parser split on the fallback |+| - malformed data, not just a crash. Wmi_Linux::effective_separator() is now the single source of truth for both call sites. Added regression coverage (tests/WmiEmptySeparatorTest.php) asserting the transport and the parser see the same effective separator, for both an empty and a custom separator.
bmfmancini
approved these changes
Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
PHPStan level 8 pass for plugin_wmi, following the same process as the prior repos in this rollout. This repo mixes a modern, already well-typed OOP transport layer (
linux_wmi.php'sLinux_WMI/Wmi_Transportclasses, from a recent PowerShell/CIM transport feature) with older, untyped procedural page/poller code - the typing work focused on the latter.(a) Error count
PHPStan level 8: 35 -> 0 errors (after adding ~25 missing Cacti-core function stubs to the scratch bootstrap).
(b) Genuine bugs fixed
functions.php(plugin_wmi_create_resource_xml()): the exported WMI query Resource XML referenced a nonexistentscript_path(scripts/wmic-script.php) andscript_function(wmic_script) - both typo'd with an extra "c". Verified against this plugin's own shipped example (templates/resource/script_server/exchange_messages.xml), which correctly usesscripts/wmi-script.php/wmi_script. Exporting and re-importing this query's resource file would have pointed the script server at a script/function that don't exist.wmi_accounts.php/wmi_queries.php:$account_actions[get_request_var('drp_action')]/$ds_actions[...]were indexed by a value only validated as some integer (viaget_filter_request_var/input_validate_input_number), not restricted to the single real bulk-action id (1). Added anarray_key_exists()-checked reject-and-redirect guard before the lookup - the same bug shape found and fixed inplugin_tags/plugin_apcupsdearlier in this rollout.poller_wmi.php:plugin_wmi_version()'sparse_ini_file()result was hardened (isset($info['info']) && is_array($info['info']) ? $info['info'] : []), and its caller (display_version()) now guards against a missingversionkey before dereferencing it - the establishedparse_ini_filehardening pattern fromplugin_apcupsd/plugin_cycle.db_fetch_row_prepared()results (functions.php,poller_wmi.php,setup.php,wmi_accounts.php,wmi_queries.php) were read via array offset without narrowing out theboolhalf of the realarray|boolreturn type - addedis_array($x) ? $x : []narrowing at each site.linux_wmi.php:explode()was called with a public, mutable$separatorclass property with no non-empty guard (a PHP 8ValueErrorif it were ever set to''at runtime);unserialize()was called onbase64_decode()'s result without checking forfalsefirst;proc_open()'s constructed command array is now wrapped inarray_values()to guarantee a list (behaviorally a no-op, satisfies PHPStan's stricterproc_open()stub).html_start_box()call sites passing'', '3'instead offalse, 3for the$div/$cell_paddingparams (the now-familiar recurring pattern across this whole rollout).poller_wmi.php:process_device($host_id)is now cast tointat its call site - CLI args are always strings, but the function's own docblock declares@param int $host_id.(c) Stubs added/verified
~25 new scratch PHPStan bootstrap stubs sourced from Cacti/cacti's
developbranch (generate_hash,get_hash_version,get_hash_data_input,data_input_method_to_xml,cacti_escapeshellarg,cacti_escapeshellcmd,array_to_sql_or,api_graph_remove_multi,api_plugin_hook_function,api_data_source_remove_multi,html_header,form_dropdown,set_request_var,__x,html_header_checkbox,number_format_i18n,get_request_var_post), plus the established reusable base set.(d) Test result
Full Pest suite: 34 passed (270 assertions), run against the real
1.2.xhost via bind-mount, unchanged before and after this pass.php-cs-fixer(Cactidevelopconfig) andphp -lboth clean on all touched files (only docblock-alignment diffs from the style pass).(e) Unresolved / flagged for maintainer judgement
variable.undefinedforwmi_accounts.php/wmi_queries.php/wmi_script.php- all 3 are top-level dispatcher scripts that read$config['base_path']immediately afterinclude('./include/auth.php'), which is what actually defines$configat runtime (every Cacti plugin page does this); PHPStan can't trace variable definitions across a plaininclude()call, so this is the same structural false-positive category as the already-establishedinclude.fileNotFoundignores.Files changed
functions.php,linux_wmi.php,poller_wmi.php,setup.php,wmi_accounts.php,wmi_queries.php,wmi_script.php,wmi_tools.php,script/wmi-script.php,CHANGELOG.md,locales/po/cacti.pot(translation template refresh only; per-language.po/.mofiles reverted to avoid Weblate churn).