Files
tinker_tickets/scripts/check_requirements.php
T
jaredandClaude Sonnet 5 1e972fe7dc Add memory_limit/max_execution_time sanity checks (#106)
config/requirements.php only checked PHP version and 6 extensions. A
deployment on a host with a low default memory_limit (e.g. shared-
hosting-style 128M) passed the startup requirements check cleanly and
only surfaced as a mysterious failure under real load — a large CSV
export, an oversized dashboard query on a big install.

Added min_memory_limit_mb (256) and min_max_execution_time (30s)
thresholds to config/requirements.php, checked as warnings (not hard
failures, since a low limit doesn't break every request) in both
scripts/check_requirements.php (CI) and api/health.php (production
monitoring). -1/0 (unlimited) always passes.

Verified the ini-size parsing and warning logic directly with
low/high/unlimited memory_limit and max_execution_time values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGDKHiU5RJdo3dqQUDow3X
2026-09-08 11:58:20 -04:00

92 lines
2.4 KiB
PHP

#!/usr/bin/env php
<?php
/**
* Verify the running PHP environment meets the declared runtime requirements.
*
* Reads config/requirements.php and checks the PHP version and that every
* required extension is loaded. Exits non-zero (failing CI) on any miss.
*
* Usage: php scripts/check_requirements.php
*/
/**
* Parse a php.ini size value (e.g. "128M", "1G", "-1") into bytes.
* Returns -1 for unlimited.
*/
function parseIniBytes(string $val): int
{
$val = trim($val);
if ($val === '' || $val === '-1') {
return -1;
}
$unit = strtolower(substr($val, -1));
$num = (int)$val;
return match ($unit) {
'g' => $num * 1024 * 1024 * 1024,
'm' => $num * 1024 * 1024,
'k' => $num * 1024,
default => $num,
};
}
$req = require __DIR__ . '/../config/requirements.php';
$errors = [];
$warnings = [];
// PHP version
$minPhp = $req['min_php_version'];
if (version_compare(PHP_VERSION, $minPhp, '<')) {
$errors[] = sprintf('PHP %s is below the required minimum %s', PHP_VERSION, $minPhp);
}
// Required extensions
foreach ($req['required_extensions'] as $ext) {
if (!extension_loaded($ext)) {
$errors[] = sprintf('Missing required PHP extension: %s', $ext);
}
}
// memory_limit / max_execution_time sanity checks (warnings, not hard
// failures — see config/requirements.php for why these matter).
$memLimitIni = ini_get('memory_limit');
$memLimitBytes = parseIniBytes($memLimitIni);
$minMemBytes = $req['min_memory_limit_mb'] * 1024 * 1024;
if ($memLimitBytes !== -1 && $memLimitBytes < $minMemBytes) {
$warnings[] = sprintf(
'memory_limit is %s, below the recommended minimum %dM',
$memLimitIni,
$req['min_memory_limit_mb']
);
}
$maxExecTime = (int)ini_get('max_execution_time');
if ($maxExecTime !== 0 && $maxExecTime < $req['min_max_execution_time']) {
$warnings[] = sprintf(
'max_execution_time is %ds, below the recommended minimum %ds',
$maxExecTime,
$req['min_max_execution_time']
);
}
if (!empty($errors)) {
fwrite(STDERR, "Requirement check FAILED:\n");
foreach ($errors as $err) {
fwrite(STDERR, ' - ' . $err . "\n");
}
exit(1);
}
foreach ($warnings as $warn) {
fwrite(STDERR, "Requirement check WARNING: " . $warn . "\n");
}
printf(
"Requirement check passed: PHP %s (>= %s); extensions: %s\n",
PHP_VERSION,
$minPhp,
implode(', ', $req['required_extensions'])
);
exit(0);