refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
36
vendor/psy/psysh/src/ManualUpdater/Checker.php
vendored
Normal file
36
vendor/psy/psysh/src/ManualUpdater/Checker.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2026 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\ManualUpdater;
|
||||
|
||||
interface Checker
|
||||
{
|
||||
const ALWAYS = 'always';
|
||||
const DAILY = 'daily';
|
||||
const WEEKLY = 'weekly';
|
||||
const MONTHLY = 'monthly';
|
||||
const NEVER = 'never';
|
||||
|
||||
/**
|
||||
* Check if the local manual is the latest version.
|
||||
*/
|
||||
public function isLatest(): bool;
|
||||
|
||||
/**
|
||||
* Get the latest available version for the configured language/format.
|
||||
*/
|
||||
public function getLatest(): string;
|
||||
|
||||
/**
|
||||
* Get the download URL for the latest manual.
|
||||
*/
|
||||
public function getDownloadUrl(): string;
|
||||
}
|
||||
164
vendor/psy/psysh/src/ManualUpdater/GitHubChecker.php
vendored
Normal file
164
vendor/psy/psysh/src/ManualUpdater/GitHubChecker.php
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2026 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\ManualUpdater;
|
||||
|
||||
use Psy\Shell;
|
||||
|
||||
class GitHubChecker implements Checker
|
||||
{
|
||||
const RELEASES_URL = 'https://api.github.com/repos/bobthecow/psysh-manual/releases';
|
||||
|
||||
private string $lang;
|
||||
private string $format;
|
||||
private ?string $currentVersion;
|
||||
private ?string $currentLang;
|
||||
private ?string $latestVersion = null;
|
||||
private ?string $downloadUrl = null;
|
||||
|
||||
/**
|
||||
* @param string $lang Language code (e.g., 'en')
|
||||
* @param string $format Format type ('php' or 'sqlite')
|
||||
* @param string|null $currentVersion Current manual version, or null if not installed
|
||||
* @param string|null $currentLang Current manual language, or null if not installed
|
||||
*/
|
||||
public function __construct(string $lang, string $format, ?string $currentVersion = null, ?string $currentLang = null)
|
||||
{
|
||||
$this->lang = $lang;
|
||||
$this->format = $format;
|
||||
$this->currentVersion = $currentVersion;
|
||||
$this->currentLang = $currentLang;
|
||||
}
|
||||
|
||||
public function isLatest(): bool
|
||||
{
|
||||
if ($this->currentVersion === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If language has changed, need to update regardless of version
|
||||
if ($this->currentLang !== null && $this->currentLang !== $this->lang) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \version_compare($this->currentVersion, $this->getLatest(), '>=');
|
||||
}
|
||||
|
||||
public function getLatest(): string
|
||||
{
|
||||
if (!isset($this->latestVersion)) {
|
||||
$this->fetchLatestRelease();
|
||||
}
|
||||
|
||||
return $this->latestVersion;
|
||||
}
|
||||
|
||||
public function getDownloadUrl(): string
|
||||
{
|
||||
if (!isset($this->downloadUrl)) {
|
||||
$this->fetchLatestRelease();
|
||||
}
|
||||
|
||||
return $this->downloadUrl;
|
||||
}
|
||||
|
||||
private function fetchLatestRelease()
|
||||
{
|
||||
$context = \stream_context_create([
|
||||
'http' => [
|
||||
'user_agent' => 'PsySH/'.Shell::VERSION,
|
||||
'timeout' => 3.0,
|
||||
],
|
||||
]);
|
||||
|
||||
\set_error_handler(function () {
|
||||
// Ignore errors - we'll handle failures below
|
||||
});
|
||||
|
||||
$result = @\file_get_contents(self::RELEASES_URL, false, $context);
|
||||
|
||||
\restore_error_handler();
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to fetch manual releases from GitHub');
|
||||
}
|
||||
|
||||
$releases = \json_decode($result, true);
|
||||
if (!$releases || !\is_array($releases)) {
|
||||
throw new \RuntimeException('Invalid response from GitHub releases API');
|
||||
}
|
||||
|
||||
// Find the first release with a manifest
|
||||
foreach ($releases as $release) {
|
||||
$manifest = $this->fetchManifest($release);
|
||||
if ($manifest === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find our language/format in the manifest
|
||||
foreach ($manifest['manuals'] as $manual) {
|
||||
if ($manual['lang'] === $this->lang && $manual['format'] === $this->format) {
|
||||
$this->latestVersion = $manual['version'];
|
||||
|
||||
// Build download URL
|
||||
$filename = \sprintf('psysh-manual-v%s-%s.tar.gz', $manual['version'], $this->lang);
|
||||
$this->downloadUrl = $release['assets_url'] ?? null;
|
||||
|
||||
// Find the actual asset URL
|
||||
foreach ($release['assets'] as $asset) {
|
||||
if ($asset['name'] === $filename) {
|
||||
$this->downloadUrl = $asset['browser_download_url'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException(\sprintf('No manual found for language "%s" in format "%s"', $this->lang, $this->format));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and parse manifest.json from a release.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function fetchManifest(array $release): ?array
|
||||
{
|
||||
// Find manifest.json in assets
|
||||
foreach ($release['assets'] as $asset) {
|
||||
if ($asset['name'] === 'manifest.json') {
|
||||
$context = \stream_context_create([
|
||||
'http' => [
|
||||
'user_agent' => 'PsySH/'.Shell::VERSION,
|
||||
'timeout' => 3.0,
|
||||
],
|
||||
]);
|
||||
|
||||
\set_error_handler(function () {
|
||||
// Ignore errors
|
||||
});
|
||||
|
||||
$manifestContent = @\file_get_contents($asset['browser_download_url'], false, $context);
|
||||
|
||||
\restore_error_handler();
|
||||
|
||||
if ($manifestContent) {
|
||||
return \json_decode($manifestContent, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
106
vendor/psy/psysh/src/ManualUpdater/Installer.php
vendored
Normal file
106
vendor/psy/psysh/src/ManualUpdater/Installer.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2026 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\ManualUpdater;
|
||||
|
||||
class Installer
|
||||
{
|
||||
private string $dataDir;
|
||||
private string $format;
|
||||
|
||||
/**
|
||||
* @param string $dataDir Data directory where manual will be installed
|
||||
* @param string $format Format type ('php' or 'sqlite')
|
||||
*/
|
||||
public function __construct(string $dataDir, string $format)
|
||||
{
|
||||
$this->dataDir = $dataDir;
|
||||
$this->format = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the data directory is writable.
|
||||
*/
|
||||
public function isDataDirWritable(): bool
|
||||
{
|
||||
return \is_dir($this->dataDir) && \is_writable($this->dataDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and install the manual from a downloaded tarball.
|
||||
*
|
||||
* @param string $tarballPath Path to the downloaded .tar.gz file
|
||||
*
|
||||
* @return bool True on success
|
||||
*/
|
||||
public function install(string $tarballPath): bool
|
||||
{
|
||||
if (!\file_exists($tarballPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create temp directory for extraction
|
||||
$tempDir = \sys_get_temp_dir().'/psysh-manual-'.\uniqid();
|
||||
if (!\mkdir($tempDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Extract tarball
|
||||
$phar = new \PharData($tarballPath);
|
||||
$phar->extractTo($tempDir);
|
||||
|
||||
// Determine the manual filename
|
||||
$manualFilename = $this->format === 'php' ? 'php_manual.php' : 'php_manual.sqlite';
|
||||
$extractedFile = $tempDir.'/'.$manualFilename;
|
||||
|
||||
if (!\file_exists($extractedFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move to data directory (overwrites existing)
|
||||
$success = \rename($extractedFile, $this->getInstallPath());
|
||||
|
||||
return $success;
|
||||
} finally {
|
||||
// Clean up temp directory
|
||||
$this->removeDirectory($tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path where the manual will be installed.
|
||||
*/
|
||||
public function getInstallPath(): string
|
||||
{
|
||||
$manualFilename = $this->format === 'php' ? 'php_manual.php' : 'php_manual.sqlite';
|
||||
|
||||
return $this->dataDir.'/'.$manualFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove a directory.
|
||||
*/
|
||||
private function removeDirectory(string $dir)
|
||||
{
|
||||
if (!\is_dir($dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$files = \array_diff(\scandir($dir), ['.', '..']);
|
||||
foreach ($files as $file) {
|
||||
$path = $dir.'/'.$file;
|
||||
\is_dir($path) ? $this->removeDirectory($path) : \unlink($path);
|
||||
}
|
||||
|
||||
\rmdir($dir);
|
||||
}
|
||||
}
|
||||
137
vendor/psy/psysh/src/ManualUpdater/IntervalChecker.php
vendored
Normal file
137
vendor/psy/psysh/src/ManualUpdater/IntervalChecker.php
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2026 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\ManualUpdater;
|
||||
|
||||
/**
|
||||
* An interval-based manual update checker.
|
||||
*
|
||||
* Caches update checks and only checks for updates at the configured interval.
|
||||
*/
|
||||
class IntervalChecker implements Checker
|
||||
{
|
||||
private Checker $checker;
|
||||
private string $cacheFile;
|
||||
private string $interval;
|
||||
private ?array $cached = null;
|
||||
|
||||
public function __construct(Checker $checker, string $cacheFile, string $interval)
|
||||
{
|
||||
$this->checker = $checker;
|
||||
$this->cacheFile = $cacheFile;
|
||||
$this->interval = $interval;
|
||||
}
|
||||
|
||||
public function isLatest(): bool
|
||||
{
|
||||
$this->loadCache();
|
||||
|
||||
// If we have a recent check, use the cached result
|
||||
if ($this->isCacheValid()) {
|
||||
return $this->cached['is_latest'] ?? true;
|
||||
}
|
||||
|
||||
// Otherwise check now and cache the result
|
||||
$isLatest = $this->checker->isLatest();
|
||||
$this->updateCache($isLatest);
|
||||
|
||||
return $isLatest;
|
||||
}
|
||||
|
||||
public function getLatest(): string
|
||||
{
|
||||
$this->loadCache();
|
||||
|
||||
// If we have a recent check, use the cached version
|
||||
if ($this->isCacheValid() && isset($this->cached['latest_version'])) {
|
||||
return $this->cached['latest_version'];
|
||||
}
|
||||
|
||||
// Otherwise fetch now and cache the result
|
||||
$latest = $this->checker->getLatest();
|
||||
$this->updateCache(null, $latest);
|
||||
|
||||
return $latest;
|
||||
}
|
||||
|
||||
public function getDownloadUrl(): string
|
||||
{
|
||||
// Always delegate to the underlying checker
|
||||
// (URL might change between checks)
|
||||
return $this->checker->getDownloadUrl();
|
||||
}
|
||||
|
||||
private function loadCache()
|
||||
{
|
||||
if ($this->cached !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = @\file_get_contents($this->cacheFile);
|
||||
if ($content) {
|
||||
$this->cached = \json_decode($content, true) ?: [];
|
||||
} else {
|
||||
$this->cached = [];
|
||||
}
|
||||
}
|
||||
|
||||
private function isCacheValid(): bool
|
||||
{
|
||||
if (!isset($this->cached['last_check'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$now = new \DateTime();
|
||||
$lastCheck = new \DateTime($this->cached['last_check']);
|
||||
|
||||
return $lastCheck >= $now->sub($this->getDateInterval());
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException if interval is not supported
|
||||
*/
|
||||
private function getDateInterval(): \DateInterval
|
||||
{
|
||||
switch ($this->interval) {
|
||||
case Checker::DAILY:
|
||||
return new \DateInterval('P1D');
|
||||
case Checker::WEEKLY:
|
||||
return new \DateInterval('P1W');
|
||||
case Checker::MONTHLY:
|
||||
return new \DateInterval('P1M');
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Invalid interval configured');
|
||||
}
|
||||
|
||||
private function updateCache(?bool $isLatest = null, ?string $latestVersion = null)
|
||||
{
|
||||
$this->loadCache();
|
||||
|
||||
// Update cache data
|
||||
$this->cached['last_check'] = \date(\DATE_ATOM);
|
||||
|
||||
if ($isLatest !== null) {
|
||||
$this->cached['is_latest'] = $isLatest;
|
||||
}
|
||||
|
||||
if ($latestVersion !== null) {
|
||||
$this->cached['latest_version'] = $latestVersion;
|
||||
}
|
||||
|
||||
// Write to file
|
||||
@\file_put_contents($this->cacheFile, \json_encode($this->cached));
|
||||
}
|
||||
}
|
||||
341
vendor/psy/psysh/src/ManualUpdater/ManualUpdate.php
vendored
Normal file
341
vendor/psy/psysh/src/ManualUpdater/ManualUpdate.php
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2026 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\ManualUpdater;
|
||||
|
||||
use Psy\ConfigPaths;
|
||||
use Psy\Configuration;
|
||||
use Psy\Exception\ErrorException;
|
||||
use Psy\Exception\InvalidManualException;
|
||||
use Psy\Manual\V2Manual;
|
||||
use Psy\Manual\V3Manual;
|
||||
use Psy\VersionUpdater\Downloader;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
/**
|
||||
* Manual update command.
|
||||
*
|
||||
* If a new manual version is available, this command will download and install it.
|
||||
*/
|
||||
class ManualUpdate
|
||||
{
|
||||
const SUCCESS = 0;
|
||||
const FAILURE = 1;
|
||||
|
||||
/** @var array{checker: Checker, installer: Installer}[] */
|
||||
private array $updates;
|
||||
private ?Downloader $downloader = null;
|
||||
|
||||
/**
|
||||
* @param array{checker: Checker, installer: Installer} ...$updates Update configuration(s)
|
||||
*/
|
||||
public function __construct(array ...$updates)
|
||||
{
|
||||
$this->updates = $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ManualUpdate instance from Configuration and command-line input.
|
||||
*
|
||||
* @param Configuration $config Configuration instance
|
||||
* @param InputInterface $input Input interface
|
||||
* @param OutputInterface $output Output interface
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromConfig(Configuration $config, InputInterface $input, OutputInterface $output): self
|
||||
{
|
||||
$lang = $input->getOption('update-manual') ?: null;
|
||||
|
||||
// Clear the manual update cache when explicitly running --update-manual
|
||||
$cacheFile = $config->getManualUpdateCheckCacheFile();
|
||||
if ($cacheFile && \file_exists($cacheFile)) {
|
||||
@\unlink($cacheFile);
|
||||
}
|
||||
|
||||
// Get current manual language before potentially deleting files
|
||||
$currentLang = null;
|
||||
$removedInvalidSqlite = false;
|
||||
$manualFile = $config->getManualDbFile();
|
||||
if ($manualFile && \file_exists($manualFile)) {
|
||||
try {
|
||||
$manual = $config->getManual();
|
||||
if ($manual) {
|
||||
$currentMeta = $manual->getMeta();
|
||||
$currentLang = $currentMeta['lang'] ?? null;
|
||||
}
|
||||
} catch (InvalidManualException $e) {
|
||||
$removedInvalidSqlite = \substr($e->getManualFile(), -7) === '.sqlite';
|
||||
self::handleInvalidManual($e, $input, $output);
|
||||
}
|
||||
}
|
||||
|
||||
$dataDir = $config->getManualInstallDir();
|
||||
if ($dataDir === false) {
|
||||
throw new \RuntimeException('Unable to find a writable data directory for manual installation');
|
||||
}
|
||||
|
||||
$phpManualPath = $dataDir.'/php_manual.php';
|
||||
$sqliteManualPath = $dataDir.'/php_manual.sqlite';
|
||||
|
||||
$formats = self::getFormatsToUpdate(
|
||||
$input,
|
||||
$output,
|
||||
\file_exists($phpManualPath),
|
||||
\file_exists($sqliteManualPath),
|
||||
$removedInvalidSqlite,
|
||||
$sqliteManualPath
|
||||
);
|
||||
|
||||
// Build update configurations for selected formats
|
||||
$checkerLang = $lang ?: $currentLang ?: 'en';
|
||||
$updates = [];
|
||||
|
||||
foreach ($formats as $format) {
|
||||
$path = $format === 'php' ? $phpManualPath : $sqliteManualPath;
|
||||
$meta = self::getManualMeta($path);
|
||||
|
||||
$updates[] = [
|
||||
'checker' => new GitHubChecker($checkerLang, $format, $meta['version'] ?? null, $meta['lang'] ?? null),
|
||||
'installer' => new Installer($dataDir, $format),
|
||||
];
|
||||
}
|
||||
|
||||
return new self(...$updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the downloader to be injected for testing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDownloader(Downloader $downloader)
|
||||
{
|
||||
$this->downloader = $downloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently set Downloader or create one based on the capabilities of the php environment.
|
||||
*
|
||||
* @throws ErrorException if a downloader cannot be created for the php environment
|
||||
*/
|
||||
private function getDownloader(): Downloader
|
||||
{
|
||||
if (!isset($this->downloader)) {
|
||||
return Downloader\Factory::getDownloader();
|
||||
}
|
||||
|
||||
return $this->downloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the manual installation.
|
||||
*/
|
||||
public function run(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
foreach ($this->updates as $update) {
|
||||
if (!$update['installer']->isDataDirWritable()) {
|
||||
$output->writeln('<error>Data directory is not writable.</error>');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
$downloader = $this->getDownloader();
|
||||
$downloader->setTempDir(\sys_get_temp_dir());
|
||||
$installed = [];
|
||||
|
||||
// Download and install each format
|
||||
foreach ($this->updates as $update) {
|
||||
$checker = $update['checker'];
|
||||
$installer = $update['installer'];
|
||||
|
||||
if ($checker->isLatest()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestVersion = $checker->getLatest();
|
||||
$downloadUrl = $checker->getDownloadUrl();
|
||||
|
||||
$output->write("Downloading manual v{$latestVersion}...");
|
||||
|
||||
try {
|
||||
$downloaded = $downloader->download($downloadUrl);
|
||||
} catch (ErrorException $e) {
|
||||
$output->write(' <error>Failed.</error>');
|
||||
$output->writeln(\sprintf('<error>%s</error>', $e->getMessage()));
|
||||
$downloader->cleanup();
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if (!$downloaded) {
|
||||
$output->writeln(' <error>Download failed.</error>');
|
||||
$downloader->cleanup();
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$output->write(' <info>OK</info>'.\PHP_EOL);
|
||||
|
||||
$downloadedFile = $downloader->getFilename();
|
||||
|
||||
if (!$installer->install($downloadedFile)) {
|
||||
$downloader->cleanup();
|
||||
$output->writeln('<error>Failed to install manual.</error>');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$installed[] = [$installer->getInstallPath(), $latestVersion];
|
||||
|
||||
$downloader->cleanup();
|
||||
}
|
||||
|
||||
if (empty($installed)) {
|
||||
$output->writeln('<info>Manual is up-to-date.</info>');
|
||||
} else {
|
||||
foreach ($installed as [$installPath, $version]) {
|
||||
$prettyPath = ConfigPaths::prettyPath($installPath);
|
||||
$output->writeln("Installed manual v{$version} to <info>{$prettyPath}</info>");
|
||||
}
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an invalid manual file by prompting the user to remove it.
|
||||
*
|
||||
* @param InvalidManualException $e The exception containing invalid manual details
|
||||
* @param InputInterface $input Input interface
|
||||
* @param OutputInterface $output Output interface
|
||||
*
|
||||
* @throws \RuntimeException if user declines to remove the file or removal fails
|
||||
*/
|
||||
private static function handleInvalidManual(InvalidManualException $e, InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$prettyPath = ConfigPaths::prettyPath($e->getManualFile());
|
||||
$output->writeln(\sprintf('<error>Invalid manual file detected:</error> <info>%s</info>', $prettyPath));
|
||||
$output->writeln('');
|
||||
|
||||
$helper = new QuestionHelper();
|
||||
$question = new ConfirmationQuestion('Remove this file and continue? [Y/n] ', true);
|
||||
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
throw new \RuntimeException('Manual update cancelled.');
|
||||
}
|
||||
|
||||
if (!\unlink($e->getManualFile())) {
|
||||
throw new \RuntimeException(\sprintf('Failed to remove file: %s', $prettyPath));
|
||||
}
|
||||
|
||||
$output->writeln('<info>Invalid manual file removed.</info>');
|
||||
$output->writeln('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user to download PHP format manual when they have/had legacy SQLite.
|
||||
*
|
||||
* @param InputInterface $input Input interface
|
||||
* @param OutputInterface $output Output interface
|
||||
* @param string $manualFile Path to current/former SQLite manual file
|
||||
* @param bool $wasRemoved Whether the file was already removed
|
||||
*
|
||||
* @return bool True if user wants to download PHP format
|
||||
*/
|
||||
private static function promptMigrateToV3(InputInterface $input, OutputInterface $output, string $manualFile, bool $wasRemoved): bool
|
||||
{
|
||||
$prettyPath = ConfigPaths::prettyPath($manualFile);
|
||||
$verb = $wasRemoved ? 'had' : 'have';
|
||||
$output->writeln(\sprintf('You %s a legacy SQLite manual: <info>%s</info>', $verb, $prettyPath));
|
||||
$output->writeln('');
|
||||
|
||||
$helper = new QuestionHelper();
|
||||
$question = new ConfirmationQuestion('Download the current manual format? [Y/n] ', true);
|
||||
|
||||
return $helper->ask($input, $output, $question);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which manual formats should be updated.
|
||||
*
|
||||
* @param InputInterface $input Input interface
|
||||
* @param OutputInterface $output Output interface
|
||||
* @param bool $hasPhpManual Whether PHP manual exists
|
||||
* @param bool $hasSqliteManual Whether SQLite manual exists
|
||||
* @param bool $removedInvalidSqlite Whether we just removed an invalid SQLite manual
|
||||
* @param string $sqliteManualPath Path to SQLite manual file
|
||||
*
|
||||
* @return string[] Array of format names to update ('php', 'sqlite')
|
||||
*/
|
||||
private static function getFormatsToUpdate(
|
||||
InputInterface $input,
|
||||
OutputInterface $output,
|
||||
bool $hasPhpManual,
|
||||
bool $hasSqliteManual,
|
||||
bool $removedInvalidSqlite,
|
||||
string $sqliteManualPath
|
||||
): array {
|
||||
// Only SQLite exists (or just removed invalid SQLite): offer to add PHP format
|
||||
if (!$hasPhpManual && ($hasSqliteManual || $removedInvalidSqlite)) {
|
||||
if (self::promptMigrateToV3($input, $output, $sqliteManualPath, $removedInvalidSqlite)) {
|
||||
return ['php', 'sqlite'];
|
||||
}
|
||||
|
||||
return ['sqlite'];
|
||||
}
|
||||
|
||||
// PHP exists, or neither exist: default to PHP, and include SQLite if it exists
|
||||
$formats = ['php'];
|
||||
if ($hasSqliteManual) {
|
||||
$formats[] = 'sqlite';
|
||||
}
|
||||
|
||||
return $formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get manual metadata from a file.
|
||||
*
|
||||
* @param string $path Path to manual file
|
||||
*
|
||||
* @return array|null Metadata array with 'version' and 'lang' keys, or null if unavailable
|
||||
*/
|
||||
private static function getManualMeta(string $path): ?array
|
||||
{
|
||||
if (!\file_exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (\substr($path, -4) === '.php') {
|
||||
$manual = new V3Manual($path);
|
||||
|
||||
return $manual->getMeta();
|
||||
}
|
||||
|
||||
if (\substr($path, -7) === '.sqlite') {
|
||||
$pdo = new \PDO('sqlite:'.$path);
|
||||
$manual = new V2Manual($pdo);
|
||||
|
||||
return $manual->getMeta();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Ignore errors reading manual metadata
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user