Skip to content

Refactor codebase for maintainability and efficiency improvements#202

Open
jwaisner wants to merge 4 commits intomainfrom
April-final-pass
Open

Refactor codebase for maintainability and efficiency improvements#202
jwaisner wants to merge 4 commits intomainfrom
April-final-pass

Conversation

@jwaisner
Copy link
Copy Markdown
Contributor


Summary

This pull request refactors and optimizes various parts of the codebase for improved maintainability, efficiency, and security.

Details of Changes

  1. Refactored service-specific logic: Updated to use getBinByName for better maintainability and reduced redundancy.
  2. Input sanitization improvement: Removed unnecessary stripslashes from GET and POST inputs in class.util.php.
  3. COM connection caching: Introduced caching and centralized reset methods for WMI and WScript.Shell COM objects within Win32Native, reducing overhead and improving efficiency.
  4. Standardized command execution: Added CommandRunner class for consistent PHP shell execution. Refactored existing shell command calls to utilize this class, improving maintainability, security, and logging.

Checklist

  • Proper testing has been conducted for all changes.
  • Documentation has been updated as necessary.
  • CI/CD pipelines pass without issues.

jwaisner added 4 commits April 9, 2026 18:20
Introduce `CommandRunner` to standardize PHP shell execution (proc_open, popen, shell_exec) with consistent argument escaping, logging, and auditable execution points. Migrate existing shell command calls across the codebase to use `CommandRunner`, ensuring improved maintainability and security.
… and centralized reset

Implement static methods to cache WMI and WScript.Shell COM objects, reducing object creation overhead and centralizing error recovery with `resetConnections`. Update existing methods to utilize cached connections for improved efficiency and maintainability.
@jwaisner jwaisner added the enhancement ✨ Improve program label Apr 10, 2026
@jwaisner jwaisner requested a review from N6REJ as a code owner April 10, 2026 00:25
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Centralize command execution, optimize COM caching, and refactor service lookup logic

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Introduce CommandRunner class for centralized, auditable shell execution with consistent
  argument escaping and logging
• Implement COM connection caching in Win32Native to reduce object creation overhead and improve
  efficiency
• Refactor service-specific logic across multiple action classes to use getBinByName() method for
  reduced redundancy
• Remove unnecessary stripslashes from GET and POST input sanitization in class.util.php
Diagram
flowchart LR
  A["Shell Execution<br/>proc_open, popen,<br/>shell_exec"] -->|"Unified via"| B["CommandRunner<br/>Class"]
  B -->|"Provides"| C["exec()<br/>stream()<br/>shellExec()<br/>background()"]
  C -->|"Logs to"| D["Batch Log"]
  
  E["Service Lookup<br/>Multiple if/elseif<br/>chains"] -->|"Refactored to"| F["getBinByName()"]
  F -->|"Used by"| G["Action Classes<br/>changePort, checkPort,<br/>enable, switchVersion"]
  
  H["COM Objects<br/>WMI, WScript.Shell"] -->|"Cached via"| I["Win32Native<br/>Static Methods"]
  I -->|"Provides"| J["getWmiCimv2()<br/>getWscriptShell()<br/>resetConnections()"]
Loading

Grey Divider

File Changes

1. core/classes/class.commandrunner.php ✨ Enhancement +190/-0

New CommandRunner class for centralized shell execution

core/classes/class.commandrunner.php


2. core/classes/actions/class.action.changePort.php ✨ Enhancement +11/-36

Refactor to use getBinByName for service lookup

core/classes/actions/class.action.changePort.php


3. core/classes/actions/class.action.checkPort.php ✨ Enhancement +8/-21

Refactor to use getBinByName for service lookup

core/classes/actions/class.action.checkPort.php


View more (8)
4. core/classes/actions/class.action.enable.php ✨ Enhancement +3/-26

Refactor to use getBinByName for service lookup

core/classes/actions/class.action.enable.php


5. core/classes/actions/class.action.loading.php ✨ Enhancement +1/-1

Replace popen with CommandRunner::background

core/classes/actions/class.action.loading.php


6. core/classes/actions/class.action.switchVersion.php ✨ Enhancement +10/-25

Refactor service refresh logic to use getBinByName

core/classes/actions/class.action.switchVersion.php


7. core/classes/bins/class.bins.php ✨ Enhancement +20/-0

Add getBinByName method for dynamic service lookup

core/classes/bins/class.bins.php


8. core/classes/class.core.php ✨ Enhancement +38/-89

Refactor unzipFile to use CommandRunner for 7zip execution

core/classes/class.core.php


9. core/classes/class.util.php 🐞 Bug fix +4/-4

Remove stripslashes and use CommandRunner for shell commands

core/classes/class.util.php


10. core/classes/class.win32native.php ✨ Enhancement +76/-17

Implement COM connection caching and centralized reset method

core/classes/class.win32native.php


11. core/classes/class.win32service.php ✨ Enhancement +3/-52

Replace execHidden with CommandRunner::execCombined for sc.exe calls

core/classes/class.win32service.php


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (1)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ☼ Reliability (1)

Grey Divider


Action required

1. Poisoned COM cache 🐞
Description
Win32Native caches COM objects (WMI/WScript.Shell) in static properties and reuses them across
calls, but COM exceptions do not clear the cached instance. After a transient COM/WMI failure,
subsequent operations may keep reusing the same broken COM object and repeatedly fail until the PHP
process restarts.
Code

core/classes/class.win32native.php[R18-75]

+    // ========================================================================
+    // COM Connection Cache
+    // Each COM object is created once per PHP process and reused across calls.
+    // Call resetConnections() if a COM operation fails and you need a fresh object.
+    // ========================================================================
+
+    /** @var COM|null Cached WMI connection to root/cimv2 (process/service queries) */
+    private static ?COM $wmiCimv2 = null;
+
+    /** @var COM|null Cached WMI connection to root/default:StdRegProv (registry key checks) */
+    private static ?COM $wmiStdRegProv = null;
+
+    /** @var COM|null Cached WScript.Shell object (registry values, shortcuts, special folders) */
+    private static ?COM $wscriptShell = null;
+
+    /**
+     * Returns the cached WMI cimv2 connection, creating it on first use.
+     */
+    private static function getWmiCimv2(): COM
+    {
+        if (self::$wmiCimv2 === null) {
+            self::$wmiCimv2 = new COM("winmgmts://./root/cimv2");
+        }
+        return self::$wmiCimv2;
+    }
+
+    /**
+     * Returns the cached WMI StdRegProv connection, creating it on first use.
+     */
+    private static function getWmiStdRegProv(): COM
+    {
+        if (self::$wmiStdRegProv === null) {
+            self::$wmiStdRegProv = new COM("winmgmts://./root/default:StdRegProv");
+        }
+        return self::$wmiStdRegProv;
+    }
+
+    /**
+     * Returns the cached WScript.Shell object, creating it on first use.
+     */
+    private static function getWscriptShell(): COM
+    {
+        if (self::$wscriptShell === null) {
+            self::$wscriptShell = new COM("WScript.Shell");
+        }
+        return self::$wscriptShell;
+    }
+
+    /**
+     * Clears all cached COM connections.
+     * Call this after a COM operation fails so the next call gets a fresh connection.
+     */
+    public static function resetConnections(): void
+    {
+        self::$wmiCimv2      = null;
+        self::$wmiStdRegProv = null;
+        self::$wscriptShell  = null;
+    }
Evidence
The new COM cache getters return a process-wide static COM instance (no per-call re-creation).
Methods such as getProcessList() wrap COM usage in try/catch but, on exception, only log/return and
do not reset the cached COM instance, so the next call will reuse the same cached object again.

core/classes/class.win32native.php[18-75]
core/classes/class.win32native.php[84-129]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Win32Native` now caches COM connections (WMI cimv2, StdRegProv, WScript.Shell). If any COM call throws, the cached object remains in the static property and will be reused, potentially causing repeated failures for the remainder of the PHP process.

### Issue Context
Caching COM objects is fine for performance, but it needs a clear invalidation strategy on failure. Currently, COM-using methods catch exceptions and return without clearing the corresponding cached COM instance.

### Fix Focus Areas
- Add cache invalidation in COM exception paths (either `Win32Native::resetConnections()` or targeted nulling of the specific cached object) in each method that uses cached COM.
- file: core/classes/class.win32native.php[18-75]
- file: core/classes/class.win32native.php[84-129]

### Suggested implementation approach
- In each `catch (Exception $e)` block that indicates a COM/WMI failure, call `self::resetConnections()` (simple + safe), then log and return.
- Optionally, be more granular (e.g., only clear `self::$wmiCimv2` when cimv2 queries fail, only clear `self::$wscriptShell` for RegRead/RegWrite failures).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +18 to +75
// ========================================================================
// COM Connection Cache
// Each COM object is created once per PHP process and reused across calls.
// Call resetConnections() if a COM operation fails and you need a fresh object.
// ========================================================================

/** @var COM|null Cached WMI connection to root/cimv2 (process/service queries) */
private static ?COM $wmiCimv2 = null;

/** @var COM|null Cached WMI connection to root/default:StdRegProv (registry key checks) */
private static ?COM $wmiStdRegProv = null;

/** @var COM|null Cached WScript.Shell object (registry values, shortcuts, special folders) */
private static ?COM $wscriptShell = null;

/**
* Returns the cached WMI cimv2 connection, creating it on first use.
*/
private static function getWmiCimv2(): COM
{
if (self::$wmiCimv2 === null) {
self::$wmiCimv2 = new COM("winmgmts://./root/cimv2");
}
return self::$wmiCimv2;
}

/**
* Returns the cached WMI StdRegProv connection, creating it on first use.
*/
private static function getWmiStdRegProv(): COM
{
if (self::$wmiStdRegProv === null) {
self::$wmiStdRegProv = new COM("winmgmts://./root/default:StdRegProv");
}
return self::$wmiStdRegProv;
}

/**
* Returns the cached WScript.Shell object, creating it on first use.
*/
private static function getWscriptShell(): COM
{
if (self::$wscriptShell === null) {
self::$wscriptShell = new COM("WScript.Shell");
}
return self::$wscriptShell;
}

/**
* Clears all cached COM connections.
* Call this after a COM operation fails so the next call gets a fresh connection.
*/
public static function resetConnections(): void
{
self::$wmiCimv2 = null;
self::$wmiStdRegProv = null;
self::$wscriptShell = null;
}
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.

Action required

1. Poisoned com cache 🐞 Bug ☼ Reliability

Win32Native caches COM objects (WMI/WScript.Shell) in static properties and reuses them across
calls, but COM exceptions do not clear the cached instance. After a transient COM/WMI failure,
subsequent operations may keep reusing the same broken COM object and repeatedly fail until the PHP
process restarts.
Agent Prompt
### Issue description
`Win32Native` now caches COM connections (WMI cimv2, StdRegProv, WScript.Shell). If any COM call throws, the cached object remains in the static property and will be reused, potentially causing repeated failures for the remainder of the PHP process.

### Issue Context
Caching COM objects is fine for performance, but it needs a clear invalidation strategy on failure. Currently, COM-using methods catch exceptions and return without clearing the corresponding cached COM instance.

### Fix Focus Areas
- Add cache invalidation in COM exception paths (either `Win32Native::resetConnections()` or targeted nulling of the specific cached object) in each method that uses cached COM.
- file: core/classes/class.win32native.php[18-75]
- file: core/classes/class.win32native.php[84-129]

### Suggested implementation approach
- In each `catch (Exception $e)` block that indicates a COM/WMI failure, call `self::resetConnections()` (simple + safe), then log and return.
- Optionally, be more granular (e.g., only clear `self::$wmiCimv2` when cimv2 queries fail, only clear `self::$wscriptShell` for RegRead/RegWrite failures).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement ✨ Improve program

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant