PHP CLI Magic: 3 Ways to Run Your Web Scripts from the Command Line with Parameters
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.
Related Contents
MySQL TIMESTAMP vs. DATETIME: The Ultimate Showdown on Time Zones, UTC, and Storage
Duration: 00:00 | DP | 2025-12-02 08:31:40The Ultimate 'Connection Refused' Guide: A PHP PDO & Docker Debugging Saga of a Forgotten Port
Duration: 00:00 | DP | 2025-12-03 09:03:20The Ultimate PHP Guide: How to Correctly Handle and Store Markdown Line Breaks from a Textarea
Duration: 00:00 | DP | 2025-11-20 08:08:00Stop Manual Debugging: A Practical Guide to Automated Testing in PHP MVC & CRUD Applications
Duration: 00:00 | DP | 2025-11-16 16:32:33Mastering PHP Switch: How to Handle Multiple Conditions for a Single Case
Duration: 00:00 | DP | 2025-11-17 09:35:40`self::` vs. `static::` in PHP: A Deep Dive into Late Static Binding
Duration: 00:00 | DP | 2025-11-18 02:38:48Recommended
Upgrading to PHP 8.4? How to Fix the `session.sid_length` Deprecation Warning
00:00 | 11Encountering `session.sid_length` and `session.sid...
Linux Command-Line Mystery: Why `ll` Hides Files like `.idea` & The Ultimate `ls` vs. `ll` Showdown
00:00 | 8Ever wondered why the `ll` command doesn't show hi...
Are Your PHP Prefixes Truly Unique? A Deep Dive into Collision Probability from `mt_rand` to `random_bytes`
00:00 | 7Generating unique identifiers in PHP is a common t...
Bootstrap Border Magic: Instantly Add Top or Bottom Borders to Elements
00:00 | 8Tired of writing custom CSS for simple 1px borders...