+ protected function ensureRequirementsMet(): void
+ {
+ $errors = [];
+
+ if (version_compare(PHP_VERSION, '8.0.2') < 0) {
+ $errors[] = "PHP >= 8.0.2 is required to install BookStack.";
+ }
+
+ $requiredExtensions = ['bcmath', 'curl', 'gd', 'iconv', 'libxml', 'mbstring', 'mysqlnd', 'xml'];
+ foreach ($requiredExtensions as $extension) {
+ if (!extension_loaded($extension)) {
+ $errors[] = "The \"{$extension}\" PHP extension is required by not active.";
+ }
+ }
+
+ try {
+ (new ProgramRunner('git', '/usr/bin/git'))->ensureFound();
+ (new ProgramRunner('php', '/usr/bin/php'))->ensureFound();
+ } catch (\Exception $exception) {
+ $errors[] = $exception->getMessage();
+ }
+
+ if (count($errors) > 0) {
+ throw new CommandError("Requirements failed with following errors:\n" . implode("\n", $errors));
+ }
+ }
+
+ protected function downloadComposerToInstall(string $installDir): void
+ {
+ $setupPath = $installDir . DIRECTORY_SEPARATOR . 'composer-setup.php';
+ $signature = file_get_contents('https://composer.github.io/installer.sig');
+ copy('https://getcomposer.org/installer', $setupPath);
+ $checksum = hash_file('sha384', $setupPath);
+
+ if ($signature !== $checksum) {
+ unlink($setupPath);
+ throw new CommandError("Could not install composer, checksum validation failed.");
+ }
+
+ $status = (new ProgramRunner('php', '/usr/bin/php'))
+ ->runWithoutOutputCallbacks([
+ $setupPath, '--quiet',
+ "--install-dir={$installDir}",
+ "--filename=composer",
+ ]);
+
+ unlink($setupPath);
+
+ if ($status !== 0) {
+ throw new CommandError("Could not install composer, composer-setup script run failed.");
+ }
+ }
+
+ /**
+ * Get the composer program.
+ */
+ protected function getComposerProgram(string $installDir): ProgramRunner