Files
InvoiceShelf/app/Platform/Operations/Installation/Application/RequirementsChecker.php
T
Darko Gjorgjijoski 7db2a8d094 feat(operations): fresh platform-operations implementation
Full rewrite of the installer (incl. the finish step), self-update pipeline,
settings store, cron webhook, dashboard/bootstrap surface, environment
manager, wizard login, config endpoint and shared helpers. Byte-compatible
public interfaces and observable behavior, including documented legacy
quirks; verified by the pilot behavioral suite and the full test suite.
2026-08-20 18:30:12 +02:00

182 lines
4.9 KiB
PHP

<?php
namespace App\Platform\Operations\Installation\Application;
use Illuminate\Support\Str;
use PDO;
use SQLite3;
/**
* Probes the runtime for the pieces the installer refuses to continue without:
* PHP extensions, web server modules and the version of whichever database
* engine the operator picked on the wizard's database step.
*/
class RequirementsChecker
{
/**
* Floor used only when a caller supplies none of its own. The real floor
* ships in config/installer.php.
*/
private const FALLBACK_MIN_PHP_VERSION = '7.0.0';
/**
* Evaluate a grouped requirement list. Only the "php" and "apache" groups
* carry meaning; anything else is dropped, as is a group that produced no
* verdicts at all. The top level "errors" flag appears only once something
* has actually failed.
*/
public function check(array $requirements): array
{
$report = [];
$missing = false;
foreach ($requirements as $group => $names) {
$verdicts = match ($group) {
'php' => $this->probeExtensions($names),
'apache' => $this->probeApacheModules($names),
default => [],
};
if ($verdicts === []) {
continue;
}
$report['requirements'][$group] = $verdicts;
if (in_array(false, $verdicts, true)) {
$missing = true;
}
}
if ($missing) {
$report['errors'] = true;
}
return $report;
}
/**
* Compare the running interpreter against a floor, reporting both the raw
* version string and its leading numeric part.
*/
public function checkPHPVersion(?string $minPhpVersion = null): array
{
$floor = ($minPhpVersion !== null && $minPhpVersion !== '')
? $minPhpVersion
: $this->getMinPhpVersion();
$running = $this->numericPhpVersion();
return [
'full' => PHP_VERSION,
'current' => $running,
'minimum' => $floor,
'supported' => version_compare($running, $floor) >= 0,
];
}
/**
* Compare a live MySQL/MariaDB connection against the floor configured for
* whichever of the two the server banner reports.
*/
public function checkMysqlVersion($conn): array
{
$banner = $conn->getAttribute(PDO::ATTR_SERVER_VERSION);
$floor = Str::contains($banner, 'MariaDB')
? config('invoiceshelf.min_mariadb_version')
: config('invoiceshelf.min_mysql_version');
return $this->versionVerdict($this->queryMysqlVersion($conn), $floor);
}
/**
* Compare the bundled SQLite library against a floor.
*/
public function checkSqliteVersion(?string $minSqliteVersion = null): array
{
return $this->versionVerdict(SQLite3::version()['versionString'], $minSqliteVersion);
}
/**
* Compare a live PostgreSQL connection against a floor.
*/
public function checkPgsqlVersion($conn, ?string $minPgsqlVersion = null): array
{
return $this->versionVerdict(pg_version($conn)['server'], $minPgsqlVersion);
}
/**
* Default PHP floor for callers that pass none.
*/
protected function getMinPhpVersion(): string
{
return self::FALLBACK_MIN_PHP_VERSION;
}
/**
* @return array<string, bool>
*/
private function probeExtensions(array $extensions): array
{
$verdicts = [];
foreach ($extensions as $extension) {
$verdicts[$extension] = extension_loaded($extension);
}
return $verdicts;
}
/**
* Module introspection is only available under mod_php; without it there
* is nothing to report, so the group is skipped rather than failed.
*
* @return array<string, bool>
*/
private function probeApacheModules(array $modules): array
{
if (! function_exists('apache_get_modules')) {
return [];
}
$enabled = apache_get_modules();
$verdicts = [];
foreach ($modules as $module) {
$verdicts[$module] = in_array($module, $enabled);
}
return $verdicts;
}
/**
* The shared shape of every database version report.
*/
private function versionVerdict(?string $current, ?string $minimum): array
{
return [
'current' => $current,
'minimum' => $minimum,
'supported' => version_compare($current, $minimum) >= 0,
];
}
/**
* PHP_VERSION with any suffix such as "-1+ubuntu" trimmed away.
*/
private function numericPhpVersion(): string
{
preg_match("#^\d+(\.\d+)*#", PHP_VERSION, $leading);
return $leading[0];
}
private function queryMysqlVersion($pdo): string
{
preg_match("/^[0-9\.]+/", $pdo->query('select version()')->fetchColumn(), $leading);
return $leading[0];
}
}