Skip to content

Instantly share code, notes, and snippets.

@Tiriel
Created September 15, 2025 09:07
Show Gist options
  • Select an option

  • Save Tiriel/f2fcae7c478d8413bc979ccd5aa48bd7 to your computer and use it in GitHub Desktop.

Select an option

Save Tiriel/f2fcae7c478d8413bc979ccd5aa48bd7 to your computer and use it in GitHub Desktop.
#!/usr/bin/env php
<?php
declare(strict_types=1);
function getSymfonyBinary(): string|null
{
return $_SERVER['SYMFONY_CLI_BINARY_NAME'] ?? null;
}
function ensureSymfonyBinary(): void
{
$symfonyBinary = getSymfonyBinary();
if ($symfonyBinary === null) {
throw new RuntimeException(
"This script must be executed through the Symfony binary 'symfony php " . __FILE__ . "'."
);
}
}
function checkPhpVersion(): void
{
$requiredVersion = '8.2.0';
$currentVersion = PHP_VERSION;
if (version_compare($currentVersion, $requiredVersion, '<')) {
throw new RuntimeException(sprintf(
"Incompatible PHP version.\n * Required version : %s or above\n * Current version : %s",
$requiredVersion,
$currentVersion
));
}
}
function checkExtensions(): void
{
$loadedExtensions = get_loaded_extensions();
$requiredExtensions = [
'curl',
'json',
'mbstring',
'xml',
'intl',
'sodium',
'zip',
['pcov', 'xdebug'],
'PDO',
'pdo_sqlite',
];
$missingExtensions = [];
foreach ($requiredExtensions as $extension) {
if (is_array($extension)) {
$diff = array_diff($extension, $loadedExtensions);
if ($diff === $extension) {
$missingExtensions[] = sprintf(
"One of : %s",
implode(', ', $extension)
);
}
continue;
}
if (!in_array($extension, $loadedExtensions, true)) {
$missingExtensions[] = $extension;
}
}
if (!empty($missingExtensions)) {
throw new RuntimeException(sprintf(
"Missing PHP extensions :\n * %s",
implode("\n * ", $missingExtensions),
));
}
}
function checkIniSettings(): void
{
$getBytes = static function (string $size): int
{
$size = trim($size);
preg_match('/(?P<value>\d+)\s*(?P<metric>[a-zA-Z]*)/', $size, $matches);
$value = $matches['value'] ?? 0;
$metric = isset($matches['metric']) ? strtolower($matches['metric']) : 'b';
$value *= match ($metric) {
'k', 'kb' => 1024,
'm', 'mb' => (1024 ** 2),
'g', 'gb' => (1024 ** 3),
't', 'tb' => (1024 ** 4),
default => 1
};
return (int) $value;
};
$iniSettings = [
'memory_limit' => ['128M', fn(string $value, string $expectedValue): bool => '-1' === $value || $getBytes($value) >= $getBytes($expectedValue)],
'max_execution_time' => ['300', fn(string $value, $expectedValue): bool => '0' === $value || (int) $value >= (int) $expectedValue],
];
foreach ($iniSettings as $setting => [$expectedValue, $assertValue]) {
$currentValue = ini_get($setting);
if (
$assertValue($currentValue, $expectedValue) === false
) {
throw new RuntimeException(sprintf(
"Non suitable PHP configuration.\nParameter : %s\nExpected value : %s\nCurrent value : %s",
$setting,
$expectedValue,
$currentValue
));
}
}
}
function checkRequirements(): void
{
$output = [];
$statusCode = 0;
$command = sprintf('%s local:check:requirements 2>&1', escapeshellarg(getSymfonyBinary()));
exec($command, $output, $statusCode);
if ($statusCode !== 0) {
$errorMessage = implode("\n", $output);
throw new RuntimeException("Symfony's requirements are not met.\n" . $errorMessage);
}
}
function checkGithubStatus(): void
{
$context = stream_context_create([
'http' => [
'timeout' => 5,
'ignore_errors' => true
]
]);
$response = @file_get_contents('https://www.githubstatus.com/api/v2/status.json', false, $context);
if ($response === false) {
throw new RuntimeException("Cannot access GitHub status.");
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException("Invalid JSON response from GitHub status.");
}
if (!isset($data['status']['indicator'])) {
throw new RuntimeException("Unexpected format of GitHub status response.");
}
if (!in_array($data['status']['indicator'], ['none', 'minor'])) {
throw new RuntimeException("GitHub is having issues : " . $data['status']['description']);
}
}
function checkPackagistStatus(): void
{
$context = stream_context_create([
'http' => [
'timeout' => 5,
'ignore_errors' => true,
'method' => 'HEAD'
]
]);
$headers = @get_headers('https://packagist.org', true, $context);
if ($headers === false) {
throw new RuntimeException("Cannot access Packagist.");
}
$statusLine = $headers[0];
if (!str_contains($statusLine, '200') && !str_contains($statusLine, '301') && !str_contains($statusLine, '302')) {
throw new RuntimeException("Packagist is not accessible. Status code : " . $statusLine);
}
}
function checkComposer(): void
{
$output = [];
$statusCode = 0;
$command = sprintf('%s composer diagnose 2>&1', escapeshellarg(getSymfonyBinary()));
exec($command, $output, $statusCode);
if ($statusCode !== 0) {
$errorMessage = implode("\n", $output);
throw new RuntimeException("Composer's requirements are not met.\n" . $errorMessage);
}
$matches = null;
preg_match('#\\nComposer version\\s*:\\s*(?P<version>[^\\n]+)#', implode("\n", $output), $matches);
$composerVersion = $matches['version'];
$requiredVersion = '2.4.0';
if (version_compare($composerVersion, $requiredVersion, '<')) {
throw new RuntimeException(sprintf(
"Incompatible Composer version.\n * Required version : %s or above\n * Current version : %s",
$requiredVersion,
$composerVersion
));
}
}
ensureSymfonyBinary();
$steps = [
checkPhpVersion(...),
checkExtensions(...),
checkIniSettings(...),
checkRequirements(...),
checkGithubStatus(...),
checkComposer(...),
checkPackagistStatus(...),
];
$errors = [];
foreach ($steps as $step) {
try {
$step();
} catch (\Throwable $e) {
$errors[] = $e;
}
}
if (!empty($errors)) {
echo "Update your local setup according to the following errors\n";
echo "=========================================================\n";
echo "\n";
foreach ($errors as $error) {
echo $error->getMessage() . "\n";
}
exit(1);
}
echo "All checks were successful !\n";
exit(0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment