Skip to content

Refactor codebase for PSR12 compliance and modernization#202

Open
bmfmancini wants to merge 26 commits intoCacti:developfrom
bmfmancini:PSR12
Open

Refactor codebase for PSR12 compliance and modernization#202
bmfmancini wants to merge 26 commits intoCacti:developfrom
bmfmancini:PSR12

Conversation

@bmfmancini
Copy link
Member

This pull request introduces a new helper module db_functions.php for SQL query logic and refines repository-wide development instructions in .github/copilot-instructions.md. The changes focus on improving maintainability, enforcing compatibility and procedural architecture, and clarifying code quality expectations. Minor updates include strict type declarations in entrypoint files.

Repository Guidance and Documentation Updates

  • Major rewrite of .github/copilot-instructions.md to clarify compatibility requirements, procedural architecture boundaries, file responsibilities, coding patterns, and CI/testing expectations. This update provides more explicit instructions for future contributors and AI agents to follow established repository conventions.

New SQL Helper Module

  • Added new file db_functions.php containing procedural helper functions for building SQL predicates, host filtering, and query fragments based on current request filters. This centralizes and standardizes common database logic used throughout the plugin.

Entrypoint Hardening

  • Added declare(strict_types=1); to images/index.php and index.php to enforce strict typing, improving code reliability and future maintainability. [1] [2]

Rename functions to match PSR12 requirements
- Change tabs to spaces
- add consts
Breaking up  some of the large files
Replaced legacy var with const/let.
Switched loose checks to strict (===, !==, strict typeof checks).
Added default param: applyFilter(action = '').
Used template literals for URL/query string assembly.
Converted many callbacks to arrow functions and shorthand methods.
Removed one duplicate object key in saveFilter payload (trim was defined twice with same value).
Kept existing flow, DOM selectors, action names, and endpoints unchanged.
Applied the Sonar-requested cleanups in monitor.js:
Refactor rending function and Ajax call functions
Lowe cognetive complexity
prepare for easier testing
Copy link
Contributor

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 modernizes the Monitor plugin by splitting previously monolithic logic into dedicated helper modules (DB/query helpers, controller/rendering, poller helpers), updating contributor guidance, and applying small entrypoint hardening changes.

Changes:

  • Added new procedural helper modules: db_functions.php, monitor_controller.php, monitor_render.php, and poller_functions.php.
  • Refactored poller_monitor.php to delegate most logic into poller_functions.php and introduced shared constants.
  • Updated .github/copilot-instructions.md and made minor entrypoint formatting/strict typing adjustments.

Reviewed changes

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

Show a summary per file
File Description
.github/copilot-instructions.md Updated repository guidance, responsibilities, and compatibility expectations.
db_functions.php New DB/query helper utilities for monitor filtering and host selection.
monitor_controller.php New controller/actions and request validation for monitor.php.
monitor_render.php New rendering/grouping functions for monitor views.
poller_functions.php New poller helper functions for uptime checks, notifications, and email formatting.
poller_monitor.php Refactored poller entrypoint to use helper module; updated CLI parsing and constants.
setup.php Schema/setup adjustments (row format, strict mode toggling, column definitions).
js/monitor.js New JS module for filter/apply/save interactions and tooltip behavior.
images/index.php Added declare(strict_types=1); to entrypoint.
index.php Minor formatting change (blank line after <?php).
locales/index.php Minor formatting change (blank line after <?php).
locales/LC_MESSAGES/index.php Added declare(strict_types=1); to entrypoint.
sounds/index.php Minor formatting change (blank line after <?php).

Comment on lines +1105 to +1114
db_execute('SET SESSION innodb_strict_mode=0');

api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor', 'type' => 'char(3)', 'NULL' => true, 'default' => 'on']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_text', 'type' => 'text', 'NULL' => false]);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_criticality', 'type' => 'tinyint', 'unsigned' => true, 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_warn', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_alert', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_icon', 'type' => 'varchar(30)', 'NULL' => false, 'default' => '']);

db_execute('SET SESSION innodb_strict_mode=1');
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

monitor_setup_table() disables innodb_strict_mode and then unconditionally re-enables it with SET SESSION innodb_strict_mode=1. This can change the session’s prior setting and, if an error occurs mid-function, may leave the session in the wrong mode. Capture the previous value (SELECT @@SESSION.innodb_strict_mode) and restore it in a finally-style path.

Suggested change
db_execute('SET SESSION innodb_strict_mode=0');
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor', 'type' => 'char(3)', 'NULL' => true, 'default' => 'on']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_text', 'type' => 'text', 'NULL' => false]);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_criticality', 'type' => 'tinyint', 'unsigned' => true, 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_warn', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_alert', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_icon', 'type' => 'varchar(30)', 'NULL' => false, 'default' => '']);
db_execute('SET SESSION innodb_strict_mode=1');
$prev_innodb_strict_mode = db_fetch_cell('SELECT @@SESSION.innodb_strict_mode');
db_execute('SET SESSION innodb_strict_mode=0');
try {
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor', 'type' => 'char(3)', 'NULL' => true, 'default' => 'on']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_text', 'type' => 'text', 'NULL' => false]);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_criticality', 'type' => 'tinyint', 'unsigned' => true, 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_warn', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_alert', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_icon', 'type' => 'varchar(30)', 'NULL' => false, 'default' => '']);
} finally {
if ($prev_innodb_strict_mode !== null) {
db_execute('SET SESSION innodb_strict_mode=' . (int) $prev_innodb_strict_mode);
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +321 to +336
foreach ($hosts as $index => $host) {
if (is_device_allowed($host['id'])) {
$host_ids[] = $host['id'];
} else {
unset($hosts[$index]);
}
}

// Determine the correct width of the cell
$maxlen = 10;

if (get_request_var('view') == 'default') {
$maxlen = db_fetch_cell('SELECT MAX(LENGTH(description))
FROM host AS h
WHERE id IN (' . implode(',', $host_ids) . ')');
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

renderTemplate() builds $host_ids[] without initializing $host_ids, and then unconditionally uses it in implode() for the MAX(LENGTH(description)) query. If filtering removes all hosts, this can produce notices and invalid SQL (IN ()). Initialize $host_ids = [] and only run the query when it has values.

Copilot uses AI. Check for mistakes.
$body = '<h1>' . __(MONITOR_PING_NOTIFICATION_SUBJECT, 'monitor') . '</h1>' . PHP_EOL;
$body_txt = __(MONITOR_PING_NOTIFICATION_SUBJECT, 'monitor') . PHP_EOL;

$message = __('The following report will identify Devices that have eclipsed their ping latency thresholds. You are receiving this report since you are subscribed to a Device associated with the Cacti system located at the following URL below.');
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

buildPingNotificationIntro() sets $message using __() without specifying the 'monitor' text domain. That string won’t be picked up by the plugin’s translations (and will likely fall back to the default domain). Pass the 'monitor' domain so localization remains consistent.

Suggested change
$message = __('The following report will identify Devices that have eclipsed their ping latency thresholds. You are receiving this report since you are subscribed to a Device associated with the Cacti system located at the following URL below.');
$message = __('The following report will identify Devices that have eclipsed their ping latency thresholds. You are receiving this report since you are subscribed to a Device associated with the Cacti system located at the following URL below.', 'monitor');

Copilot uses AI. Check for mistakes.
Comment on lines +188 to +203
foreach ($hosts as $index => $host) {
if (is_device_allowed($host['id'])) {
$host_ids[] = $host['id'];
} else {
unset($hosts[$index]);
}
}

// Determine the correct width of the cell
$maxlen = 10;

if (get_request_var('view') == 'default') {
$maxlen = db_fetch_cell('SELECT MAX(LENGTH(description))
FROM host AS h
WHERE id IN (' . implode(',', $host_ids) . ')');
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

renderSite() builds $host_ids[] without initializing $host_ids, and then unconditionally uses it in implode() for the MAX(LENGTH(description)) query. If no devices are allowed, this can raise notices and generate invalid SQL (IN ()). Initialize $host_ids = [] and guard the query when the list is empty.

Copilot uses AI. Check for mistakes.
} else {
strURL = 'monitor.php?header=false';

if (action >= '') {
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

applyFilter() uses if (action >= ''), which is always true for strings and will append &action= even when action is empty. Use an explicit emptiness check (e.g., action !== '') so requests don’t include a blank action parameter.

Suggested change
if (action >= '') {
if (action !== '') {

Copilot uses AI. Check for mistakes.
myTimer = setTimeout(timeStep, 1000);

$(globalThis).resize(() => {
$(document).tooltip('option', 'position', { my: '1eft:15 top', at: 'right center' });
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

The tooltip resize handler sets the position using my: '1eft:15 top' (digit “1” instead of “l”). This typo prevents the intended tooltip positioning on resize. Change it to left:15 top to match the initial tooltip config.

Suggested change
$(document).tooltip('option', 'position', { my: '1eft:15 top', at: 'right center' });
$(document).tooltip('option', 'position', { my: 'left:15 top', at: 'right center' });

Copilot uses AI. Check for mistakes.
} else {
$('#timer').html(value);
// What is a second, well if you are an
// imperial storm tropper, it's just a little more than a second.
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Spelling in the timeStep() comment: “storm tropper” should be “stormtrooper”.

Suggested change
// imperial storm tropper, it's just a little more than a second.
// imperial stormtrooper, it's just a little more than a second.

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +86
switch ($arg) {
case '--version':
case '-V':
case '-v':
displayVersion();
exit;
case '--help':
case '-H':
case '-h':
displayHelp();
exit;
case '--debug':
$debug = true;

break;
default:
print 'ERROR: Invalid Parameter ' . $parameter . PHP_EOL . PHP_EOL;
displayHelp();
exit;
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

poller_monitor.php no longer accepts the previously supported --force flag; it now exits with “Invalid Parameter” when passed. If this flag is used by existing automation, this is a breaking CLI change. Consider keeping --force as a no-op (or implement the intended behavior) to preserve backward compatibility.

Copilot uses AI. Check for mistakes.
TheWitness
TheWitness previously approved these changes Mar 3, 2026
@TheWitness
Copy link
Member

You might want to run phpcsfixit on this.

somethingwithproof added a commit to somethingwithproof/plugin_monitor that referenced this pull request Mar 3, 2026
- Fix SQL injection in rfilter RLIKE expression by escaping with db_qstr()
- Add missing JOIN ON clause in GROUP_CONCAT query (syntax error)
- Initialize $host_ids array before loop to prevent undefined variable errors
- Move sort() outside foreach loop for O(n log n) instead of O(n² log n)
- Fix JavaScript comparison: change 'action >= ""' to 'action !== ""'
- Fix tooltip positioning typo: '1eft' -> 'left'
- Fix spelling: 'tropper' -> 'stormtrooper'

Addresses review feedback from PR Cacti#202.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
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.

4 participants