user(); if (!$user || !$user->roles()->where('name', 'ROLE_ADMIN')->exists()) { return response()->json(['message' => 'Forbidden'], 403); } $phpDefaultPath = $this->resolveBinary('php'); $phpConfiguredPath = trim((string) Setting::where('key', 'system.php_binary')->value('value')); $phpSelectedPath = $phpConfiguredPath ?: (PHP_BINARY ?: $phpDefaultPath); $phpSelectedOk = (bool) $phpSelectedPath; $phpSelectedVersion = $phpSelectedPath ? ($this->resolvePhpVersion($phpSelectedPath) ?? PHP_VERSION) : PHP_VERSION; $minVersions = $this->resolveMinVersions(); $composerPath = $this->resolveBinary('composer'); $nodePath = $this->resolveBinary('node'); $npmPath = $this->resolveBinary('npm'); $tarPath = $this->resolveBinary('tar'); $rsyncPath = $this->resolveBinary('rsync'); $procFunctions = [ 'proc_open', 'proc_get_status', 'proc_close', ]; $disabledFunctions = array_filter(array_map('trim', explode(',', (string) ini_get('disable_functions')))); $disabledLookup = array_fill_keys($disabledFunctions, true); $procFunctionStatus = []; foreach ($procFunctions as $function) { $procFunctionStatus[$function] = function_exists($function) && !isset($disabledLookup[$function]); } return response()->json([ 'php' => PHP_VERSION, 'php_default' => $phpDefaultPath, 'php_configured' => $phpConfiguredPath ?: null, 'php_selected_path' => $phpSelectedPath, 'php_selected_ok' => $phpSelectedOk, 'php_selected_version' => $phpSelectedVersion, 'min_versions' => $minVersions, 'composer' => $composerPath, 'composer_version' => $this->resolveBinaryVersion($composerPath, ['--version']), 'node' => $nodePath, 'node_version' => $this->resolveBinaryVersion($nodePath, ['--version']), 'npm' => $npmPath, 'npm_version' => $this->resolveBinaryVersion($npmPath, ['--version']), 'tar' => $tarPath, 'tar_version' => $this->resolveBinaryVersion($tarPath, ['--version']), 'rsync' => $rsyncPath, 'rsync_version' => $this->resolveBinaryVersion($rsyncPath, ['--version']), 'proc_functions' => $procFunctionStatus, 'storage_writable' => is_writable(storage_path()), 'updates_writable' => is_writable(storage_path('app/updates')) || @mkdir(storage_path('app/updates'), 0755, true), ]); } private function resolveBinary(string $name): ?string { $process = new Process(['sh', '-lc', "command -v {$name}"]); $process->setTimeout(5); $process->run(); if (!$process->isSuccessful()) { return null; } $output = trim($process->getOutput()); return $output !== '' ? $output : null; } private function resolvePhpVersion(string $path): ?string { $process = new Process([$path, '-r', 'echo PHP_VERSION;']); $process->setTimeout(5); $process->run(); if (!$process->isSuccessful()) { return null; } $output = trim($process->getOutput()); return $output !== '' ? $output : null; } private function resolveBinaryVersion(?string $path, array $args): ?string { if (!$path) { return null; } $process = new Process(array_merge([$path], $args)); $process->setTimeout(5); $process->run(); if (!$process->isSuccessful()) { return null; } $output = trim($process->getOutput()); if ($output === '') { return null; } $line = strtok($output, "\n") ?: $output; if (preg_match('/(\\d+\\.\\d+(?:\\.\\d+)?)/', $line, $matches)) { return $matches[1]; } return null; } private function resolveMinVersions(): array { $composerJson = $this->readJson(base_path('composer.json')); $packageJson = $this->readJson(base_path('package.json')); $php = $composerJson['require']['php'] ?? null; $node = $packageJson['engines']['node'] ?? null; $npm = $packageJson['engines']['npm'] ?? null; $composer = $composerJson['require']['composer-runtime-api'] ?? null; return [ 'php' => is_string($php) ? $php : null, 'node' => is_string($node) ? $node : null, 'npm' => is_string($npm) ? $npm : null, 'composer' => is_string($composer) ? $composer : null, ]; } private function readJson(string $path): array { if (!is_file($path)) { return []; } $contents = file_get_contents($path); if ($contents === false) { return []; } $data = json_decode($contents, true); return is_array($data) ? $data : []; } }