Created
September 15, 2025 09:07
-
-
Save Tiriel/f2fcae7c478d8413bc979ccd5aa48bd7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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