PHP CLI Magic: 3 Ways to Run Your Web Scripts from the Command Line with Parameters

Published: 2025-11-11
Author: DP
Views: 14
Category: PHP
Content
## The Scenario In daily development, we often need to run a PHP controller action, which works perfectly in a web environment, as a scheduled task via `Crontab`. For example, a function for daily data statistics. The web request URL might look like this: `http://lib00.com/statistics/daily-pv-cal?date=2025-10-30` The controller code relies on `$_GET['date']` to get the date parameter: ```php // php_app/Controllers/Backend/StatisticsController.php class StatisticsController { /** * Daily PV statistics (incremental update) * Crontab: 10 0 * * * */ public function dailyPVCal(): void { set_time_limit(600); ini_set('memory_limit', '512M'); // Core issue: How to get the 'date' parameter in CLI mode? $statDate = $_GET['date'] ?? date('Y-m-d', strtotime('-1 day')); // ...business logic... } } ``` The entry file `index.php` already has a simple check for CLI mode but doesn't handle parameter passing yet: ```php // php_app/public_backend/index.php <?php // Detect CLI mode if (php_sapi_name() === 'cli') { // Simulate HTTP environment $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_URI'] = $argv[1] ?? '/sitemap/generate'; // $argv[1] is the first argument after the script $_SERVER['HTTP_HOST'] = 'wiki.lib00.com'; // Use wiki.lib00.com as an identifier } // ...framework loading logic... ``` The core challenge is: How can we elegantly pass parameters (like `date=2025-10-30`) to the script in CLI mode and ensure that `$_GET['date']` in `StatisticsController` receives the value correctly without modifying the controller's logic? Here are three effective solutions compiled by DP@lib00. --- ## The Solutions ### Solution 1: Simulate the Full Request URI (Recommended) This is the simplest and most intuitive method because it closely mimics the original HTTP request, requiring minimal changes to your existing code. **Modify `index.php`:** Add parsing for the query string within the `REQUEST_URI` in the environment simulation block. ```php // php_app/public_backend/index.php if (php_sapi_name() === 'cli') { $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_URI'] = $argv[1] ?? '/sitemap/generate'; $_SERVER['HTTP_HOST'] = 'wiki.lib00.com'; // New: Parse the query string from the URL and populate $_GET $urlParts = parse_url($_SERVER['REQUEST_URI']); if (isset($urlParts['query'])) { parse_str($urlParts['query'], $_GET); } } ``` **Command-Line Invocation:** Pass the entire URI path (including query parameters) as a single string argument. It's best to wrap it in quotes, especially if the URI contains special characters like `&`. ```bash php index.php "/statistics/daily-pv-cal?date=2025-10-30" ``` ### Solution 2: Use `getopt` for Named Arguments `getopt` is a built-in PHP function designed for parsing command-line options, making it a standard way to build proper CLI tools. **Modify `index.php`:** ```php // php_app/public_backend/index.php if (php_sapi_name() === 'cli') { // Parse command-line options like --uri=... --date=... $options = getopt('', ['uri::', 'date::']); $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_URI'] = $options['uri'] ?? $argv[1] ?? '/sitemap/generate'; $_SERVER['HTTP_HOST'] = 'wiki.lib00.com'; // Convert command-line options to $_GET if (isset($options['date'])) { $_GET['date'] = $options['date']; } // Can be extended to support more parameters } ``` **Command-Line Invocation:** This approach is clearer, as the parameters and their values are explicit. ```bash php index.php --uri=/statistics/daily-pv-cal --date=2025-10-30 ``` ### Solution 3: Manually Parse `$argv` This is a more flexible but somewhat primitive method that involves iterating through the `$argv` array to manually parse `key=value` formatted arguments. **Modify `index.php`:** ```php // php_app/public_backend/index.php if (php_sapi_name() === 'cli') { $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_URI'] = $argv[1] ?? '/sitemap/generate'; $_SERVER['HTTP_HOST'] = 'wiki.lib00.com'; // Parse key=value arguments starting from $argv[2] for ($i = 2; $i < count($argv); $i++) { if (strpos($argv[$i], '=') !== false) { list($key, $value) = explode('=', $argv[$i], 2); $_GET[trim($key)] = $value; } } } ``` **Command-Line Invocation:** Each `key=value` pair is a separate argument. ```bash php index.php /statistics/daily-pv-cal date=2025-10-30 ``` --- ## Crontab Integration Example Using the recommended Solution 1, you can configure your `Crontab` task as follows. ```cron # Run at 00:10 every day to process the previous day's data # Note: '%' must be escaped as '\%' in crontab 10 0 * * * cd /path/to/wiki.lib00/php_app/public_backend && php index.php "/statistics/daily-pv-cal?date=$(date -d 'yesterday' +\%Y-\%m-\%d)" >> /var/log/daily-pv.log 2>&1 ``` If your PHP script already has logic to handle a default date (like `date('Y-m-d', strtotime('-1 day'))`), you can also omit the `date` parameter: ```cron 10 0 * * * cd /path/to/wiki.lib00/php_app/public_backend && php index.php "/statistics/daily-pv-cal" >> /var/log/daily-pv.log 2>&1 ``` --- ## Optimization Tip For easier debugging and monitoring, it's a good practice to add a check for the CLI environment within your controller method and output relevant log messages. ```php // php_app/Controllers/Backend/StatisticsController.php public function dailyPVCal(): void { // ... $statDate = $_GET['date'] ?? date('Y-m-d', strtotime('-1 day')); // Output logs in CLI mode for better tracking if (php_sapi_name() === 'cli') { echo "[" . date('Y-m-d H:i:s') . "] Starting statistics for: {$statDate}\n"; } // ... Business logic ... if (php_sapi_name() === 'cli') { echo "[" . date('Y-m-d H:i:s') . "] Statistics completed.\n"; } } ``` --- ## Conclusion Using web scripts for CLI automation is a common requirement. By cleverly simulating the HTTP environment and parsing command-line arguments at the entry point, we can easily reuse existing business logic. Among the three methods presented, **Solution 1 (Simulating the Full Request URI)** is the preferred choice for most scenarios due to its simplicity and non-invasive nature.