diff --git a/.drevops/installer/src/Bag/AbstractBag.php b/.drevops/installer/src/Bag/AbstractBag.php index e77064c8e..802227dff 100644 --- a/.drevops/installer/src/Bag/AbstractBag.php +++ b/.drevops/installer/src/Bag/AbstractBag.php @@ -3,7 +3,9 @@ namespace DrevOps\Installer\Bag; /** + * Class AbstractBag. * + * Bag to store and manipulate items. */ abstract class AbstractBag { diff --git a/.drevops/installer/src/Bag/Answers.php b/.drevops/installer/src/Bag/Answers.php index c4a0b3bee..afb085abf 100644 --- a/.drevops/installer/src/Bag/Answers.php +++ b/.drevops/installer/src/Bag/Answers.php @@ -5,7 +5,9 @@ use DrevOps\Installer\Trait\SingletonTrait; /** + * Class Answers. * + * Bag to store answers. */ class Answers extends AbstractBag { diff --git a/.drevops/installer/src/Bag/Config.php b/.drevops/installer/src/Bag/Config.php index 89b376691..4ff041f2a 100644 --- a/.drevops/installer/src/Bag/Config.php +++ b/.drevops/installer/src/Bag/Config.php @@ -8,7 +8,9 @@ use DrevOps\Installer\Utils\Files; /** + * Class Config. * + * Bag to store configuration. */ class Config extends AbstractBag { @@ -49,16 +51,7 @@ public function __construct() { } /** - * Set installer configuration. - * - * Installer config is a config of this installer script. For configs of the - * project being installed, @param $name. - * - * @param $value - * - * @see set_answer() - * - * @see init_config() + * {@inheritdoc} */ public function set(string $name, mixed $value): void { if ($this->isReadOnly()) { @@ -89,19 +82,21 @@ public function fromValues($values = []): AbstractBag { /** * Shorthand to get the value of whether install should be quiet. * - * @return mixed|null + * @return bool + * Whether install should be quiet. */ - public function isQuiet() { - return $this->get('quiet', FALSE); + public function isQuiet(): bool { + return (bool) $this->get('quiet', FALSE); } /** * Shorthand to get the value the destination directory. * - * @return mixed|null + * @return string + * The destination directory. */ public function getDstDir(): string { - return $this->get(Env::INSTALLER_DST_DIR, getcwd()); + return (string) $this->get(Env::INSTALLER_DST_DIR, getcwd()); } /** @@ -111,6 +106,12 @@ public function getWebroot(): string { return $this->get(Env::WEBROOT, 'web'); } + /** + * Collect values from environment. + * + * @param array $defaults + * Default values. + */ protected function collectFromEnv(array $defaults = []): void { $constants = Env::getConstants(); diff --git a/.drevops/installer/src/Command/InstallCommand.php b/.drevops/installer/src/Command/InstallCommand.php index 85ad808e7..4fb31158e 100644 --- a/.drevops/installer/src/Command/InstallCommand.php +++ b/.drevops/installer/src/Command/InstallCommand.php @@ -53,21 +53,29 @@ class InstallCommand extends Command { protected static $defaultName = 'install'; /** + * Config. + * * @var \DrevOps\Installer\Bag\Config */ protected $config; /** + * Prompt manager. + * * @var \DrevOps\Installer\Prompt\PromptManager */ - protected $questionManager; + protected $promptManager; /** + * Install manager. + * * @var \DrevOps\Installer\InstallManager */ protected $installManager; /** + * Print manager. + * * @var \DrevOps\Installer\PrintManager */ protected $printManager; @@ -86,7 +94,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->config = Config::getInstance()->fromValues(['cwd' => getcwd()] + $input->getArguments() + $input->getOptions()); $io = $this->initIo($input, $output); - $this->questionManager = new PromptManager($io, $this->config); + $this->promptManager = new PromptManager($io, $this->config); $this->printManager = new PrintManager($io, $this->config); return $this->doExecute(); @@ -117,8 +125,11 @@ protected function doExecute(): int { return self::SUCCESS; } + /** + * Ask questions and return a bag of answers. + */ protected function askQuestions(): AbstractBag { - $this->questionManager->askQuestions(function (PromptManager $qm): void { + $this->promptManager->askQuestions(function (PromptManager $qm): void { $is_installed = InstallManager::isInstalled($this->config->getDstDir()); // // @todo remove this. @@ -197,18 +208,24 @@ protected function askQuestions(): AbstractBag { }); if (!$this->config->isQuiet()) { - $summary = $this->questionManager->getAnswersSummary(); + $summary = $this->promptManager->getAnswersSummary(); $this->printManager->printSummary($summary, 'INSTALLATION SUMMARY'); } - return $this->questionManager->getAnswers(); + return $this->promptManager->getAnswers(); } - protected function askShouldProceed() { + /** + * Ask whether to proceed with the installation. + * + * @return bool + * Whether to proceed with the installation. + */ + protected function askShouldProceed(): bool { $proceed = TRUE; if (!$this->config->isQuiet()) { - $proceed = $this->questionManager->ask(ProceedDrevopsInstallPrompt::ID); + $proceed = $this->promptManager->ask(ProceedDrevopsInstallPrompt::ID); } // Kill-switch to not proceed with install. If false, the installation will @@ -221,7 +238,7 @@ protected function askShouldProceed() { } /** - * Initialise output. + * Initialise the input/output. * * @param \Symfony\Component\Console\Output\OutputInterface $output * Output. diff --git a/.drevops/installer/src/InstallManager.php b/.drevops/installer/src/InstallManager.php index 032f902b5..4f99b55a4 100644 --- a/.drevops/installer/src/InstallManager.php +++ b/.drevops/installer/src/InstallManager.php @@ -7,9 +7,10 @@ use DrevOps\Installer\Utils\Executor; use DrevOps\Installer\Utils\Files; use DrevOps\Installer\Utils\Output; +use DrevOps\Installer\Utils\Tokenizer; /** - * + * Install manager. */ class InstallManager { @@ -18,8 +19,18 @@ class InstallManager { */ final public const INSTALLER_DRUPAL_VERSION = 9; + /** + * Download manager. + * + * @var \DrevOps\Installer\Utils\Downloader + */ protected $downloadManager; + /** + * Config. + * + * @var \DrevOps\Installer\Bag\Config + */ protected $config; /** @@ -34,16 +45,35 @@ public static function isInstalled(string $dst_dir): bool { } public function install($config): void { + $this->config = $config; + + $this->checkRequirements(); + + $this->downloadManager->download(); + + $this->prepareDestination(); + + Tokenizer::replaceTokens(); + + $this->copyFiles(); + + ProcessorManager::processDemo(); } + /** + * Check requirements. + */ public function checkRequirements(): void { - Executor::commandExists('git'); - Executor::commandExists('tar'); - Executor::commandExists('composer'); + Executor::validateCommandExists('git'); + Executor::validateCommandExists('tar'); + Executor::validateCommandExists('composer'); } + /** + * Prepare destination directory. + */ public function prepareDestination(): void { - $dst = Config::getDstDir(); + $dst = Config::getInstance()->getDstDir(); if (!is_dir($dst)) { Output::status(sprintf('Creating destination directory "%s".', $dst), Output::INSTALLER_STATUS_MESSAGE, FALSE); @@ -69,8 +99,11 @@ public function prepareDestination(): void { Output::status('Done', Output::INSTALLER_STATUS_SUCCESS); } + /** + * Copy files. + */ public function copyFiles(): void { - $src = Config::get(Env::INSTALLER_TMP_DIR); + $src = Config::getInstance()->get(Env::INSTALLER_TMP_DIR); $dst = Config::getDstDir(); // Due to the way symlinks can be ordered, we cannot copy files one-by-one diff --git a/.drevops/installer/src/InstallerApp.php b/.drevops/installer/src/InstallerApp.php index bc2f5a5f1..25be00967 100644 --- a/.drevops/installer/src/InstallerApp.php +++ b/.drevops/installer/src/InstallerApp.php @@ -6,10 +6,13 @@ use Symfony\Component\Console\Application; /** - * + * Installer application. */ class InstallerApp extends Application { + /** + * {@inheritdoc} + */ public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') { parent::__construct($name, $version); diff --git a/.drevops/installer/src/PrintManager.php b/.drevops/installer/src/PrintManager.php index a5f662673..ec103a1ea 100644 --- a/.drevops/installer/src/PrintManager.php +++ b/.drevops/installer/src/PrintManager.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; /** - * + * Print manager. */ class PrintManager { @@ -25,6 +25,9 @@ public function printHeader(): void { } } + /** + * Print header for interactive mode. + */ public function printHeaderInteractive(): void { $commit = $this->config->get(Env::INSTALLER_COMMIT); @@ -53,6 +56,9 @@ public function printHeaderInteractive(): void { Formatter::printBox($this->io, $content, 'WELCOME TO DREVOPS INTERACTIVE INSTALLER'); } + /** + * Print header for quiet mode. + */ public function printHeaderQuiet(): void { $commit = $this->config->get(Env::INSTALLER_COMMIT); @@ -78,19 +84,33 @@ public function printHeaderQuiet(): void { $this->io->setVerbosity($verbosity_level); } + /** + * Print summary. + * + * @param array $values + * The values to print. + * @param string $title + * The title. + */ public function printSummary($values, $title): void { - $values = array_map(static function ($key, $value) : array { - return [$key => $value]; + $values = array_map(static function ($key, $value): array { + return [$key => $value]; }, array_keys($values), $values); $this->io->writeln(sprintf(' %s', $title)); $this->io->definitionList(...$values); } + /** + * Print abort message. + */ public function printAbort(): void { Formatter::printBox($this->io, 'Aborting DrevOps installation. No files were changed.', 'INSTALLATION ABORTED'); } + /** + * Print footer. + */ public function printFooter(): void { $output = ''; diff --git a/.drevops/installer/src/Processor/DatabaseDownloadSourceProcessor.php b/.drevops/installer/src/Processor/DatabaseDownloadSourceProcessor.php index 678e732ba..40b2a537b 100644 --- a/.drevops/installer/src/Processor/DatabaseDownloadSourceProcessor.php +++ b/.drevops/installer/src/Processor/DatabaseDownloadSourceProcessor.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Database download source processor. */ class DatabaseDownloadSourceProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/DatabaseImageProcessor.php b/.drevops/installer/src/Processor/DatabaseImageProcessor.php index 23fe54c2a..74151fb42 100644 --- a/.drevops/installer/src/Processor/DatabaseImageProcessor.php +++ b/.drevops/installer/src/Processor/DatabaseImageProcessor.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Database download source processor. */ class DatabaseImageProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/DemoModeProcessor.php b/.drevops/installer/src/Processor/DemoModeProcessor.php index 799bd32d7..36087b92c 100644 --- a/.drevops/installer/src/Processor/DemoModeProcessor.php +++ b/.drevops/installer/src/Processor/DemoModeProcessor.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Demo mode processor. */ class DemoModeProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/DemoProcessor.php b/.drevops/installer/src/Processor/DemoProcessor.php index d501b0ed2..95bd58bee 100644 --- a/.drevops/installer/src/Processor/DemoProcessor.php +++ b/.drevops/installer/src/Processor/DemoProcessor.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Demo processor. */ class DemoProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/DeployTypeProcessor.php b/.drevops/installer/src/Processor/DeployTypeProcessor.php index f37bca5c8..5815775b8 100644 --- a/.drevops/installer/src/Processor/DeployTypeProcessor.php +++ b/.drevops/installer/src/Processor/DeployTypeProcessor.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Deploy type processor. */ class DeployTypeProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/DrevopsInternalProcess.php b/.drevops/installer/src/Processor/DrevopsInternalProcess.php index 3db8aab17..ee768741a 100644 --- a/.drevops/installer/src/Processor/DrevopsInternalProcess.php +++ b/.drevops/installer/src/Processor/DrevopsInternalProcess.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * DrevOps internal processor. */ class DrevopsInternalProcess extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/EnableCommentedCodeProcessor.php b/.drevops/installer/src/Processor/EnableCommentedCodeProcessor.php index 5e054a532..28a1b0029 100644 --- a/.drevops/installer/src/Processor/EnableCommentedCodeProcessor.php +++ b/.drevops/installer/src/Processor/EnableCommentedCodeProcessor.php @@ -8,7 +8,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Enable commented code processor. */ class EnableCommentedCodeProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/OverrideExistingDbProcessor.php b/.drevops/installer/src/Processor/OverrideExistingDbProcessor.php index 7ba9b8155..5bfed542a 100644 --- a/.drevops/installer/src/Processor/OverrideExistingDbProcessor.php +++ b/.drevops/installer/src/Processor/OverrideExistingDbProcessor.php @@ -8,7 +8,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Override existing db processor. */ class OverrideExistingDbProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/PreserveAcquiaProcessor.php b/.drevops/installer/src/Processor/PreserveAcquiaProcessor.php index 5d1964ede..32744e9ed 100644 --- a/.drevops/installer/src/Processor/PreserveAcquiaProcessor.php +++ b/.drevops/installer/src/Processor/PreserveAcquiaProcessor.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Preserve Acquia processor. */ class PreserveAcquiaProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/PreserveDocCommentsProcessor.php b/.drevops/installer/src/Processor/PreserveDocCommentsProcessor.php index f6c2a901a..12d440150 100644 --- a/.drevops/installer/src/Processor/PreserveDocCommentsProcessor.php +++ b/.drevops/installer/src/Processor/PreserveDocCommentsProcessor.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Preserve doc comments processor. */ class PreserveDocCommentsProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/PreserveDrevopsInfoProcessor.php b/.drevops/installer/src/Processor/PreserveDrevopsInfoProcessor.php index 5bcd2724c..f4411541d 100644 --- a/.drevops/installer/src/Processor/PreserveDrevopsInfoProcessor.php +++ b/.drevops/installer/src/Processor/PreserveDrevopsInfoProcessor.php @@ -8,7 +8,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Preserve DrevOps info processor. */ class PreserveDrevopsInfoProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/PreserveFtpProcessor.php b/.drevops/installer/src/Processor/PreserveFtpProcessor.php index 01a2f56bf..18666e23c 100644 --- a/.drevops/installer/src/Processor/PreserveFtpProcessor.php +++ b/.drevops/installer/src/Processor/PreserveFtpProcessor.php @@ -8,7 +8,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Preserve FTP processor. */ class PreserveFtpProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/PreserveLagoonProcessor.php b/.drevops/installer/src/Processor/PreserveLagoonProcessor.php index fbbb6bdb3..1c68d9ea3 100644 --- a/.drevops/installer/src/Processor/PreserveLagoonProcessor.php +++ b/.drevops/installer/src/Processor/PreserveLagoonProcessor.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Preserve Lagoon processor. */ class PreserveLagoonProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/PreserveRenovatebotProcessor.php b/.drevops/installer/src/Processor/PreserveRenovatebotProcessor.php index 0760b3d89..87edf73a4 100644 --- a/.drevops/installer/src/Processor/PreserveRenovatebotProcessor.php +++ b/.drevops/installer/src/Processor/PreserveRenovatebotProcessor.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Preserve Renovatebot processor. */ class PreserveRenovatebotProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/ProfileProcessor.php b/.drevops/installer/src/Processor/ProfileProcessor.php index 4a2f855e6..a65882659 100644 --- a/.drevops/installer/src/Processor/ProfileProcessor.php +++ b/.drevops/installer/src/Processor/ProfileProcessor.php @@ -7,7 +7,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Profile processor. */ class ProfileProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/ProvisionUseProfileProcessor.php b/.drevops/installer/src/Processor/ProvisionUseProfileProcessor.php index d8bb5d6bb..20e1083ce 100644 --- a/.drevops/installer/src/Processor/ProvisionUseProfileProcessor.php +++ b/.drevops/installer/src/Processor/ProvisionUseProfileProcessor.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Provision use profile processor. */ class ProvisionUseProfileProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/StringTokensProcessor.php b/.drevops/installer/src/Processor/StringTokensProcessor.php index 1d0d83f15..9cb8b9fc8 100644 --- a/.drevops/installer/src/Processor/StringTokensProcessor.php +++ b/.drevops/installer/src/Processor/StringTokensProcessor.php @@ -9,7 +9,7 @@ use function Symfony\Component\String\u; /** - * + * String tokens processor. */ class StringTokensProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/Processor/WebrootProcessor.php b/.drevops/installer/src/Processor/WebrootProcessor.php index b2685f597..71d425945 100644 --- a/.drevops/installer/src/Processor/WebrootProcessor.php +++ b/.drevops/installer/src/Processor/WebrootProcessor.php @@ -7,7 +7,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * + * Webroot processor. */ class WebrootProcessor extends AbstractProcessor { diff --git a/.drevops/installer/src/ProcessorManager.php b/.drevops/installer/src/ProcessorManager.php index c42e59d68..1a4602833 100644 --- a/.drevops/installer/src/ProcessorManager.php +++ b/.drevops/installer/src/ProcessorManager.php @@ -2,28 +2,27 @@ namespace DrevOps\Installer; +use DrevOps\Installer\Bag\Config; use DrevOps\Installer\Processor\AbstractProcessor; use DrevOps\Installer\Utils\ClassLoader; /** + * Processor manager. * + * Orchestrates the processing of all processors. */ class ProcessorManager { /** * Config object. - * - * @var \DrevOps\Installer\Bag\Config */ - protected $config; + protected Config $config; /** * @param \Symfony\Component\Console\Output\OutputInterface $output */ - public function __construct($config, /** - * Output object. - */ - protected $output) { + public function __construct(Config $config, + protected $output) { $this->config = clone $config; $this->config->setReadOnly(); } @@ -34,12 +33,12 @@ public function __construct($config, /** public function process(): void { $classes = ClassLoader::load('Processor', AbstractProcessor::class); - $classes = array_filter($classes, static function ($class) : bool { - return $class::$weight > 0; + $classes = array_filter($classes, static function ($class): bool { + return $class::$weight > 0; }); - usort($classes, static function ($a, $b) : int { - return $a::$weight <=> $b::$weight; + usort($classes, static function ($a, $b): int { + return $a::$weight <=> $b::$weight; }); foreach ($classes as $class) { diff --git a/.drevops/installer/src/Prompt/AbstractChoicePrompt.php b/.drevops/installer/src/Prompt/AbstractChoicePrompt.php index 930949dc1..d8933a25d 100644 --- a/.drevops/installer/src/Prompt/AbstractChoicePrompt.php +++ b/.drevops/installer/src/Prompt/AbstractChoicePrompt.php @@ -11,10 +11,15 @@ use Symfony\Component\Console\Question\Question; /** - * + * Abstract class for choice prompts. */ abstract class AbstractChoicePrompt extends AbstractPrompt { + /** + * Whether the prompt is multiselect. + * + * @var bool + */ protected $isMultiselect = FALSE; /** @@ -23,7 +28,7 @@ abstract class AbstractChoicePrompt extends AbstractPrompt { * @return array * The choices array with keys as shortopts and values as longopts. */ - abstract public static function choices(); + abstract public static function choices(): array; /** * {@inheritdoc} diff --git a/.drevops/installer/src/Prompt/AbstractConfirmationPrompt.php b/.drevops/installer/src/Prompt/AbstractConfirmationPrompt.php index 0579207fe..ae355fcd5 100644 --- a/.drevops/installer/src/Prompt/AbstractConfirmationPrompt.php +++ b/.drevops/installer/src/Prompt/AbstractConfirmationPrompt.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Question\Question; /** - * + * Abstract confirmation prompt. */ abstract class AbstractConfirmationPrompt extends AbstractPrompt { diff --git a/.drevops/installer/src/Prompt/AbstractPrompt.php b/.drevops/installer/src/Prompt/AbstractPrompt.php index 895c90188..c06ee153e 100644 --- a/.drevops/installer/src/Prompt/AbstractPrompt.php +++ b/.drevops/installer/src/Prompt/AbstractPrompt.php @@ -8,7 +8,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; /** - * + * Abstract prompt. */ abstract class AbstractPrompt { diff --git a/.drevops/installer/src/Prompt/Concrete/DatabaseDownloadSourcePrompt.php b/.drevops/installer/src/Prompt/Concrete/DatabaseDownloadSourcePrompt.php index 244c427f9..7732fa38e 100644 --- a/.drevops/installer/src/Prompt/Concrete/DatabaseDownloadSourcePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/DatabaseDownloadSourcePrompt.php @@ -9,10 +9,15 @@ use DrevOps\Installer\Utils\Env; /** - * + * Database download source prompt. */ class DatabaseDownloadSourcePrompt extends AbstractChoicePrompt { + /** + * The prompt ID. + */ + final const ID = 'database_download_source'; + final const CHOICE_URL = 'url'; final const CHOICE_FTP = 'ftp'; @@ -23,8 +28,6 @@ class DatabaseDownloadSourcePrompt extends AbstractChoicePrompt { final const CHOICE_NONE = 'none'; - final const ID = 'database_download_source'; - /** * {@inheritdoc} */ diff --git a/.drevops/installer/src/Prompt/Concrete/DatabaseImagePrompt.php b/.drevops/installer/src/Prompt/Concrete/DatabaseImagePrompt.php index 98b4bb8fe..b7293b27e 100644 --- a/.drevops/installer/src/Prompt/Concrete/DatabaseImagePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/DatabaseImagePrompt.php @@ -11,10 +11,13 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Database image prompt. */ class DatabaseImagePrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'database_image'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/DatabaseStoreTypePrompt.php b/.drevops/installer/src/Prompt/Concrete/DatabaseStoreTypePrompt.php index b3de365ac..02e9c56ff 100644 --- a/.drevops/installer/src/Prompt/Concrete/DatabaseStoreTypePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/DatabaseStoreTypePrompt.php @@ -9,27 +9,30 @@ use DrevOps\Installer\Utils\Env; /** - * + * Database store type prompt. */ class DatabaseStoreTypePrompt extends AbstractChoicePrompt { + /** + * The prompt ID. + */ + final const ID = 'database_store_type'; + final const CHOICE_FILE = 'file'; final const CHOICE_DOCKER_IMAGE = 'docker_image'; - final const ID = 'database_store_type'; - /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Database store type'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return ' When developing locally, do you want to import the database dump from the file or store it imported in the docker image for faster builds?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/DeployTypePrompt.php b/.drevops/installer/src/Prompt/Concrete/DeployTypePrompt.php index fcdc91e98..40303796b 100644 --- a/.drevops/installer/src/Prompt/Concrete/DeployTypePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/DeployTypePrompt.php @@ -12,10 +12,15 @@ use Symfony\Component\Console\Style\SymfonyStyle; /** - * + * Deploy type prompt. */ class DeployTypePrompt extends AbstractChoicePrompt { + /** + * The prompt ID. + */ + final const ID = 'deploy_type'; + final const CHOICE_ARTIFACT = 'artifact'; final const CHOICE_WEBHOOK = 'webhook'; @@ -26,8 +31,6 @@ class DeployTypePrompt extends AbstractChoicePrompt { final const CHOICE_NONE = 'none'; - final const ID = 'deploy_type'; - public function __construct(SymfonyStyle $io) { parent::__construct($io); $this->isMultiselect = TRUE; @@ -36,14 +39,14 @@ public function __construct(SymfonyStyle $io) { /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Deployment'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return 'How do you deploy your code to the hosting?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/MachineNamePrompt.php b/.drevops/installer/src/Prompt/Concrete/MachineNamePrompt.php index c849afd2d..27cf210db 100644 --- a/.drevops/installer/src/Prompt/Concrete/MachineNamePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/MachineNamePrompt.php @@ -10,23 +10,26 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Machine name prompt. */ class MachineNamePrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'machine_name'; /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Machine name'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return 'What is your site machine name?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/ModulePrefixPrompt.php b/.drevops/installer/src/Prompt/Concrete/ModulePrefixPrompt.php index a4926d848..db818e864 100644 --- a/.drevops/installer/src/Prompt/Concrete/ModulePrefixPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/ModulePrefixPrompt.php @@ -10,23 +10,26 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Module prefix prompt. */ class ModulePrefixPrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'module_prefix'; /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Module prefix'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return 'What is your project-specific module prefix?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/NamePrompt.php b/.drevops/installer/src/Prompt/Concrete/NamePrompt.php index 3dc3c0da6..9d41dea74 100644 --- a/.drevops/installer/src/Prompt/Concrete/NamePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/NamePrompt.php @@ -11,23 +11,26 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Name prompt. */ class NamePrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'name'; /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Site name'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return 'What is your site name?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/OrgMachineNamePrompt.php b/.drevops/installer/src/Prompt/Concrete/OrgMachineNamePrompt.php index d4a900b9a..41e970c02 100644 --- a/.drevops/installer/src/Prompt/Concrete/OrgMachineNamePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/OrgMachineNamePrompt.php @@ -10,23 +10,26 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Organisation machine name prompt. */ class OrgMachineNamePrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'org_machine_name'; /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Organisation machine name'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return 'What is your organization machine name?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/OrgPrompt.php b/.drevops/installer/src/Prompt/Concrete/OrgPrompt.php index c359f1fc8..7a9a19e0b 100644 --- a/.drevops/installer/src/Prompt/Concrete/OrgPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/OrgPrompt.php @@ -10,23 +10,26 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Organisation prompt. */ class OrgPrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'org'; /** * {@inheritdoc} */ - public static function title():string { + public static function title(): string { return 'Organisation'; } /** * {@inheritdoc} */ - public static function question():string { + public static function question(): string { return 'What is your organization name?'; } diff --git a/.drevops/installer/src/Prompt/Concrete/OverrideExistingDbPrompt.php b/.drevops/installer/src/Prompt/Concrete/OverrideExistingDbPrompt.php index 4dc3fd10c..4017c41c3 100644 --- a/.drevops/installer/src/Prompt/Concrete/OverrideExistingDbPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/OverrideExistingDbPrompt.php @@ -9,10 +9,13 @@ use DrevOps\Installer\Utils\Env; /** - * + * Override existing database prompt. */ class OverrideExistingDbPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'override_existing_db'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/PreserveAcquiaPrompt.php b/.drevops/installer/src/Prompt/Concrete/PreserveAcquiaPrompt.php index b7737f734..76802c463 100644 --- a/.drevops/installer/src/Prompt/Concrete/PreserveAcquiaPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/PreserveAcquiaPrompt.php @@ -10,10 +10,13 @@ use DrevOps\Installer\Utils\Formatter; /** - * + * Preserve Acquia prompt. */ class PreserveAcquiaPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'preserve_acquia'; /** @@ -54,7 +57,7 @@ protected function discoveredValue(Config $config, Answers $answers): mixed { * {@inheritdoc} */ public static function getFormattedValue(mixed $value): string { - return Formatter::formatEnabled($value); + return Formatter::formatEnabledDisabled($value); } } diff --git a/.drevops/installer/src/Prompt/Concrete/PreserveDocCommentsPrompt.php b/.drevops/installer/src/Prompt/Concrete/PreserveDocCommentsPrompt.php index 556fd8fdf..3a5489ec4 100644 --- a/.drevops/installer/src/Prompt/Concrete/PreserveDocCommentsPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/PreserveDocCommentsPrompt.php @@ -8,10 +8,13 @@ use DrevOps\Installer\Utils\Files; /** - * + * Preserve Acquia prompt. */ class PreserveDocCommentsPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'preserve_doc_comments'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/PreserveDrevopsInfoPrompt.php b/.drevops/installer/src/Prompt/Concrete/PreserveDrevopsInfoPrompt.php index 4d3cafb55..55867f9a4 100644 --- a/.drevops/installer/src/Prompt/Concrete/PreserveDrevopsInfoPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/PreserveDrevopsInfoPrompt.php @@ -8,10 +8,13 @@ use DrevOps\Installer\Utils\Files; /** - * + * Preserve Acquia prompt. */ class PreserveDrevopsInfoPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'preserve_drevops_info'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/PreserveFtpPrompt.php b/.drevops/installer/src/Prompt/Concrete/PreserveFtpPrompt.php index 026862fc8..07a0322e8 100644 --- a/.drevops/installer/src/Prompt/Concrete/PreserveFtpPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/PreserveFtpPrompt.php @@ -10,10 +10,13 @@ use DrevOps\Installer\Utils\Formatter; /** - * + * Preserve FTP prompt. */ class PreserveFtpPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'preserve_ftp'; /** @@ -50,7 +53,7 @@ protected function discoveredValue(Config $config, Answers $answers): mixed { * {@inheritdoc} */ public static function getFormattedValue(mixed $value): string { - return Formatter::formatEnabled($value); + return Formatter::formatEnabledDisabled($value); } } diff --git a/.drevops/installer/src/Prompt/Concrete/PreserveLagoonPrompt.php b/.drevops/installer/src/Prompt/Concrete/PreserveLagoonPrompt.php index db1050150..9a2e6297c 100644 --- a/.drevops/installer/src/Prompt/Concrete/PreserveLagoonPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/PreserveLagoonPrompt.php @@ -9,10 +9,13 @@ use DrevOps\Installer\Utils\Formatter; /** - * + * Preserve FTP prompt. */ class PreserveLagoonPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'preserve_lagoon'; /** @@ -63,7 +66,7 @@ protected function discoveredValue(Config $config, Answers $answers): mixed { * {@inheritdoc} */ public static function getFormattedValue(mixed $value): string { - return Formatter::formatEnabled($value); + return Formatter::formatEnabledDisabled($value); } } diff --git a/.drevops/installer/src/Prompt/Concrete/PreserveRenovatebotPrompt.php b/.drevops/installer/src/Prompt/Concrete/PreserveRenovatebotPrompt.php index 78e73947a..c6b95a4ed 100644 --- a/.drevops/installer/src/Prompt/Concrete/PreserveRenovatebotPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/PreserveRenovatebotPrompt.php @@ -9,10 +9,13 @@ use DrevOps\Installer\Utils\Formatter; /** - * + * Preserve Renovatebot prompt. */ class PreserveRenovatebotPrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'preserve_renovatebot'; /** @@ -51,7 +54,7 @@ protected function discoveredValue(Config $config, Answers $answers): mixed { * {@inheritdoc} */ public static function getFormattedValue(mixed $value): string { - return Formatter::formatEnabled($value); + return Formatter::formatEnabledDisabled($value); } } diff --git a/.drevops/installer/src/Prompt/Concrete/ProceedDrevopsInstallPrompt.php b/.drevops/installer/src/Prompt/Concrete/ProceedDrevopsInstallPrompt.php index 4d5ac0009..41c6b8128 100644 --- a/.drevops/installer/src/Prompt/Concrete/ProceedDrevopsInstallPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/ProceedDrevopsInstallPrompt.php @@ -8,10 +8,13 @@ use Symfony\Component\Console\Question\Question; /** - * + * Prompt to confirm the installation. */ class ProceedDrevopsInstallPrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'proceed_drevops_install'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/ProfilePrompt.php b/.drevops/installer/src/Prompt/Concrete/ProfilePrompt.php index ef1e4116f..64252a2cd 100644 --- a/.drevops/installer/src/Prompt/Concrete/ProfilePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/ProfilePrompt.php @@ -11,10 +11,13 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Profile prompt. */ class ProfilePrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'profile'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/ProvisionUseProfilePrompt.php b/.drevops/installer/src/Prompt/Concrete/ProvisionUseProfilePrompt.php index 83e0cacf2..fa4d7bdae 100644 --- a/.drevops/installer/src/Prompt/Concrete/ProvisionUseProfilePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/ProvisionUseProfilePrompt.php @@ -9,10 +9,13 @@ use DrevOps\Installer\Utils\Env; /** - * + * Preserve FTP prompt. */ class ProvisionUseProfilePrompt extends AbstractConfirmationPrompt { + /** + * The prompt ID. + */ final const ID = 'provision_use_profile'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/ThemePrompt.php b/.drevops/installer/src/Prompt/Concrete/ThemePrompt.php index f505eab61..eff061025 100644 --- a/.drevops/installer/src/Prompt/Concrete/ThemePrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/ThemePrompt.php @@ -13,10 +13,13 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Theme prompt. */ class ThemePrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'theme'; /** @@ -70,7 +73,7 @@ protected function discoveredValue(Config $config, Answers $answers): mixed { return NULL; } - if ($value) { + if ($value !== '' && $value !== '0') { $value = basename((string) $value); $value = str_replace(['.info.yml', '.info'], '', $value); } diff --git a/.drevops/installer/src/Prompt/Concrete/UrlPrompt.php b/.drevops/installer/src/Prompt/Concrete/UrlPrompt.php index 7eb2b4bb0..236676750 100644 --- a/.drevops/installer/src/Prompt/Concrete/UrlPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/UrlPrompt.php @@ -9,10 +9,13 @@ use DrevOps\Installer\Utils\Validator; /** - * + * URL prompt. */ class UrlPrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'url'; /** diff --git a/.drevops/installer/src/Prompt/Concrete/WebrootPrompt.php b/.drevops/installer/src/Prompt/Concrete/WebrootPrompt.php index ff7dde5dd..1440a3547 100644 --- a/.drevops/installer/src/Prompt/Concrete/WebrootPrompt.php +++ b/.drevops/installer/src/Prompt/Concrete/WebrootPrompt.php @@ -13,10 +13,13 @@ use DrevOps\Installer\Utils\Validator; /** - * + * Web root prompt. */ class WebrootPrompt extends AbstractPrompt { + /** + * The prompt ID. + */ final const ID = 'webroot'; /** @@ -68,7 +71,7 @@ protected function validator(mixed $value, Config $config, Answers $answers): vo * {@inheritdoc} */ protected function valueNormalizer($value, Config $config, Answers $answers): mixed { - return trim((string) Strings::toMachineName($value), '/'); + return trim(Strings::toMachineName($value), '/'); } } diff --git a/.drevops/installer/src/Prompt/PromptManager.php b/.drevops/installer/src/Prompt/PromptManager.php index 07dae6959..3042d8a52 100644 --- a/.drevops/installer/src/Prompt/PromptManager.php +++ b/.drevops/installer/src/Prompt/PromptManager.php @@ -10,17 +10,37 @@ use function Symfony\Component\String\u; /** - * + * Prompt manager. */ class PromptManager { + /** + * A bag of answers. + */ protected AbstractBag $answers; + /** + * Prompt manager constructor. + * + * @param \Symfony\Component\Console\Style\SymfonyStyle $io + * The Symfony style. + * @param \DrevOps\Installer\Bag\Config $config + * The config. + */ public function __construct(protected SymfonyStyle $io, protected Config $config) { // Always get a fresh bag of answers. $this->answers = Answers::getInstance()->clear(); } + /** + * Ask questions. + * + * @param mixed $callback + * The callback to ask questions. + * + * @return $this + * The prompt manager. + */ public function askQuestions(mixed $callback): static { // If the callback is set, invoke it. if (!is_callable($callback)) { @@ -32,6 +52,17 @@ public function askQuestions(mixed $callback): static { return $this; } + /** + * Ask a question. + * + * @param string $question_id + * The question ID. + * @param bool $is_quiet + * Whether to ask the question quietly. + * + * @return mixed + * The answer. + */ public function ask(string $question_id, $is_quiet = FALSE) { $class = $this->getPromptClass($question_id); @@ -47,14 +78,42 @@ public function ask(string $question_id, $is_quiet = FALSE) { return $answer; } + /** + * Get the answers. + * + * @return \DrevOps\Installer\Bag\AbstractBag + * The answers bag. + */ public function getAnswers(): AbstractBag { return $this->answers; } - public function getAnswer(string $question_id, $default = NULL) { + /** + * Get an answer. + * + * @param string $question_id + * The question ID. + * @param mixed $default + * The default value. + * + * @return mixed + * The answer. + */ + public function getAnswer(string $question_id, mixed $default = NULL) { return $this->answers->get($question_id, $default); } + /** + * Set an answer. + * + * @param string $question_id + * The question ID. + * @param mixed $value + * The value. + * + * @return $this + * The prompt manager. + */ public function setAnswer(string $question_id, mixed $value): static { $this->answers->set($question_id, $value); @@ -62,7 +121,10 @@ public function setAnswer(string $question_id, mixed $value): static { } /** + * Get the answers summary. + * * @return mixed[] + * The answers summary. */ public function getAnswersSummary(): array { $values = []; @@ -76,6 +138,15 @@ public function getAnswersSummary(): array { return $values; } + /** + * Get the prompt class. + * + * @param string|null $id + * The prompt ID. + * + * @return string + * The prompt class. + */ protected function getPromptClass(?string $id): string { $classes = ClassLoader::load('Prompt', AbstractPrompt::class); diff --git a/.drevops/installer/src/Trait/ReadOnlyTrait.php b/.drevops/installer/src/Trait/ReadOnlyTrait.php index 3acef7bc0..b1ec4360b 100644 --- a/.drevops/installer/src/Trait/ReadOnlyTrait.php +++ b/.drevops/installer/src/Trait/ReadOnlyTrait.php @@ -3,16 +3,33 @@ namespace DrevOps\Installer\Trait; /** - * + * Read only trait. */ trait ReadOnlyTrait { + /** + * The read only flag. + * + * @var bool + */ protected $readOnly = FALSE; + /** + * Sets the read only flag. + * + * @param bool $value + * The value. + */ public function setReadOnly($value = TRUE): void { $this->readOnly = $value; } + /** + * Returns the read only flag. + * + * @return bool + * The value. + */ public function isReadOnly(): bool { return $this->readOnly; } diff --git a/.drevops/installer/src/Trait/SingletonTrait.php b/.drevops/installer/src/Trait/SingletonTrait.php index 7ffb37368..dcdd7fc21 100644 --- a/.drevops/installer/src/Trait/SingletonTrait.php +++ b/.drevops/installer/src/Trait/SingletonTrait.php @@ -9,7 +9,6 @@ * Classes extending this abstract class will inherit Singleton behavior. * * @phpstan-consistent-constructor - * @package Merlin\Reporting\Utils */ trait SingletonTrait { @@ -27,6 +26,9 @@ trait SingletonTrait { */ protected static $calledInternally = FALSE; + /** + * Constructor. + */ public function __construct() { if (!self::$calledInternally) { throw new \Exception('Cannot instantiate Singleton class directly. Use ::getInstance() instead.'); @@ -49,10 +51,16 @@ final public static function getInstance(): static { return static::$instance; } + /** + * Cloning of Singleton is disallowed. + */ final public function __clone() { throw new \Exception('Cloning of Singleton is disallowed.'); } + /** + * Unserializing instances of Singleton classes is disallowed. + */ final public function __wakeup() { throw new \Exception('Unserializing instances of Singleton classes is disallowed.'); } diff --git a/.drevops/installer/src/Utils/Arrays.php b/.drevops/installer/src/Utils/Arrays.php index 9fd1252c8..f5bff11de 100644 --- a/.drevops/installer/src/Utils/Arrays.php +++ b/.drevops/installer/src/Utils/Arrays.php @@ -3,7 +3,7 @@ namespace DrevOps\Installer\Utils; /** - * + * Array utilities. */ class Arrays { @@ -60,10 +60,11 @@ public static function sortByKeyArray(array $array, array $keys): array { public static function sortByValueArray(array $array, array $values): array { $sorted = $array; - uasort($sorted, static function ($a, $b) use ($values) : int { - $a_index = array_search($a, $values, TRUE); - $b_index = array_search($b, $values, TRUE); - return ($a_index === FALSE ? PHP_INT_MAX : $a_index) <=> ($b_index === FALSE ? PHP_INT_MAX : $b_index); + uasort($sorted, static function ($a, $b) use ($values): int { + $a_index = array_search($a, $values, TRUE); + $b_index = array_search($b, $values, TRUE); + + return ($a_index === FALSE ? PHP_INT_MAX : $a_index) <=> ($b_index === FALSE ? PHP_INT_MAX : $b_index); }); // Preserve non-numeric keys and reset numeric ones. @@ -80,6 +81,17 @@ public static function sortByValueArray(array $array, array $values): array { return $result; } + /** + * Reindex array. + * + * @param array $values + * The array to reindex. + * @param int $start + * The start index. + * + * @return array + * The reindexed array. + */ public static function reindex(array $values, $start): array { return empty($values) ? [] diff --git a/.drevops/installer/src/Utils/ClassLoader.php b/.drevops/installer/src/Utils/ClassLoader.php index 4a9cda875..9eff4100d 100644 --- a/.drevops/installer/src/Utils/ClassLoader.php +++ b/.drevops/installer/src/Utils/ClassLoader.php @@ -3,7 +3,7 @@ namespace DrevOps\Installer\Utils; /** - * Utilities. + * Class loader. */ class ClassLoader { @@ -58,7 +58,7 @@ public static function load(string $path, $parent_class = NULL): array { * @return array * Array of loaded class instances. */ - public static function filterByClass($parent_class, $classes = NULL) { + public static function filterByClass($parent_class, $classes = NULL): array { $classes = $classes ?? get_declared_classes(); foreach ($classes as $k => $class) { diff --git a/.drevops/installer/src/Utils/ConstantsLoader.php b/.drevops/installer/src/Utils/ConstantsLoader.php index 8f6044ab0..9df79bba9 100644 --- a/.drevops/installer/src/Utils/ConstantsLoader.php +++ b/.drevops/installer/src/Utils/ConstantsLoader.php @@ -3,17 +3,32 @@ namespace DrevOps\Installer\Utils; /** + * Constants loader. * + * Loads constants from a class. */ class ConstantsLoader { + /** + * Load constants from a class. + * + * @param object|string $class + * The class name or object. + * @param string|null $prefix + * The prefix to filter constants by. + * @param bool $prefix_is_key + * Whether the prefix is a key or a value. + * + * @return array + * The constants. + */ public static function load(object|string $class, string|null $prefix = NULL, $prefix_is_key = TRUE): array { $reflection = new \ReflectionClass($class); $constants = $reflection->getConstants(); if ($prefix) { - $constants = array_filter($constants, static function ($value, $key) use ($prefix, $prefix_is_key) : bool { - return str_starts_with($prefix_is_key ? $key : $value, $prefix); + $constants = array_filter($constants, static function ($value, $key) use ($prefix, $prefix_is_key): bool { + return str_starts_with($prefix_is_key ? $key : $value, $prefix); }, ARRAY_FILTER_USE_BOTH); } diff --git a/.drevops/installer/src/Utils/DotEnv.php b/.drevops/installer/src/Utils/DotEnv.php index 1c30daa67..9afa6d925 100644 --- a/.drevops/installer/src/Utils/DotEnv.php +++ b/.drevops/installer/src/Utils/DotEnv.php @@ -3,10 +3,19 @@ namespace DrevOps\Installer\Utils; /** - * + * Dotenv utilities. */ class DotEnv { + /** + * Parse .env file. + * + * @param string $filename + * The filename. + * + * @return array|false + * The parsed .env file or FALSE if the file is not readable. + */ public static function parseDotenv($filename = '.env'): false|array { if (!is_readable($filename)) { return FALSE; @@ -19,6 +28,14 @@ public static function parseDotenv($filename = '.env'): false|array { return parse_ini_string($contents); } + /** + * Load .env file. + * + * @param string $filename + * The filename. + * @param bool $override_existing + * Whether to override existing environment variables. + */ public static function loadDotenv($filename = '.env', $override_existing = FALSE): void { $parsed = DotEnv::parseDotenv($filename); @@ -45,7 +62,20 @@ public static function loadDotenv($filename = '.env', $override_existing = FALSE } } - public static function getValueFromDstDotenv(string $dst_dir, $name, $default = NULL) { + /** + * Get value from .env file. + * + * @param string $dst_dir + * The destination directory. + * @param string $name + * The name of the variable. + * @param mixed $default + * The default value. + * + * @return mixed + * The value. + */ + public static function getValueFromDstDotenv(string $dst_dir, $name, mixed $default = NULL): mixed { // Environment variables always take precedence. $env_value = Env::get($name, NULL); if (!is_null($env_value)) { diff --git a/.drevops/installer/src/Utils/Downloader.php b/.drevops/installer/src/Utils/Downloader.php index 0f09990f6..3c42548ee 100644 --- a/.drevops/installer/src/Utils/Downloader.php +++ b/.drevops/installer/src/Utils/Downloader.php @@ -3,9 +3,10 @@ namespace DrevOps\Installer\Utils; use DrevOps\Installer\Bag\Config; +use Symfony\Component\Console\Output\Output; /** - * + * Downloader. */ class Downloader { @@ -28,11 +29,11 @@ public static function findLatestDrevopsRelease($org, $project, $release_prefix) } public static function downloadRemote(): void { - $dst = Config::get(Env::DREVOPS_INSTALLER_TMP_DIR); + $dst = Config::getInstance()->get(Env::INSTALLER_TMP_DIR); $org = 'drevops'; $project = 'drevops'; - $ref = Config::get(Env::DREVOPS_INSTALLER_COMMIT); - $release_prefix = Config::get(Env::DREVOPS_VERSION); + $ref = Config::getInstance()->get(Env::INSTALLER_COMMIT); + $release_prefix = Config::getInstance()->get(Env::DREVOPS_VERSION); if ($ref == 'HEAD') { $ref = Downloader::findLatestDrevopsRelease($org, $project, $release_prefix); @@ -52,9 +53,9 @@ public static function downloadRemote(): void { } public static function downloadLocal(): void { - $dst = Config::get(Env::DREVOPS_INSTALLER_TMP_DIR); - $repo = Config::get(Env::DREVOPS_INSTALLER_LOCAL_REPO); - $ref = Config::get(Env::DREVOPS_INSTALLER_COMMIT); + $dst = Config::getInstance()->get(Env::DREVOPS_INSTALLER_TMP_DIR); + $repo = Config::getInstance()->get(Env::DREVOPS_INSTALLER_LOCAL_REPO); + $ref = Config::getInstance()->get(Env::DREVOPS_INSTALLER_COMMIT); Output::status(sprintf('Downloading DrevOps from the local repository "%s" at ref "%s".', $repo, $ref), Output::INSTALLER_STATUS_MESSAGE, FALSE); @@ -77,7 +78,7 @@ public static function downloadLocal(): void { * Download DrevOps source files. */ public function download(): void { - if (Config::get(Env::DREVOPS_INSTALLER_LOCAL_REPO)) { + if (Config::getInstance()->get(Env::DREVOPS_INSTALLER_LOCAL_REPO)) { Downloader::downloadLocal(); } else { diff --git a/.drevops/installer/src/Utils/Env.php b/.drevops/installer/src/Utils/Env.php index 213b27827..ebd7152c7 100644 --- a/.drevops/installer/src/Utils/Env.php +++ b/.drevops/installer/src/Utils/Env.php @@ -69,7 +69,7 @@ public static function get($name, $default = NULL) { /** * Get all constants defined in this class. */ - public static function getConstants():array { + public static function getConstants(): array { return ConstantsLoader::load(__CLASS__, 'DREVOPS_', FALSE); } diff --git a/.drevops/installer/src/Utils/Executor.php b/.drevops/installer/src/Utils/Executor.php index a2000c328..4da21dc57 100644 --- a/.drevops/installer/src/Utils/Executor.php +++ b/.drevops/installer/src/Utils/Executor.php @@ -3,7 +3,7 @@ namespace DrevOps\Installer\Utils; /** - * + * Executor. */ class Executor { @@ -20,7 +20,16 @@ public static function doExec($command, array &$output = NULL, &$return_var = NU return exec($command, $output, $return_var); } - public static function commandExists(string $command): void { + /** + * Check if command exists. + * + * @param string $command + * The command to check. + * + * @throws \RuntimeException + * If the command does not exist. + */ + public static function validateCommandExists(string $command): void { static::doExec('command -v ' . $command, $lines, $ret); if ($ret === 1) { throw new \RuntimeException(sprintf('Command "%s" does not exist in the current environment.', $command)); diff --git a/.drevops/installer/src/Utils/Files.php b/.drevops/installer/src/Utils/Files.php index 65b87812f..cc4e235f0 100644 --- a/.drevops/installer/src/Utils/Files.php +++ b/.drevops/installer/src/Utils/Files.php @@ -3,12 +3,27 @@ namespace DrevOps\Installer\Utils; /** - * + * Files utility. */ class Files { - public static function copyRecursive($source, $dest, $permissions = 0755, $copy_empty_dirs = FALSE): bool { - $parent = dirname((string) $dest); + /** + * Copy a file, or recursively copy a folder and its contents. + * + * @param string $source + * Source path. + * @param string $dest + * Destination path. + * @param int $permissions + * New folder creation permissions. + * @param bool $copy_empty_dirs + * Whether to copy empty directories. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public static function copyRecursive(string $source, string $dest, int $permissions = 0755, bool $copy_empty_dirs = FALSE): bool { + $parent = dirname($dest); if (!is_dir($parent)) { mkdir($parent, $permissions, TRUE); @@ -21,8 +36,8 @@ public static function copyRecursive($source, $dest, $permissions = 0755, $copy_ $cur_dir = getcwd(); chdir($parent); $ret = TRUE; - if (!is_readable(basename((string) $dest))) { - $ret = symlink(readlink($source), basename((string) $dest)); + if (!is_readable(basename($dest))) { + $ret = symlink(readlink($source), basename($dest)); } chdir($cur_dir); @@ -55,25 +70,48 @@ public static function copyRecursive($source, $dest, $permissions = 0755, $copy_ return TRUE; } - public static function rmdirRecursiveEmpty($directory, $options = []): void { + /** + * Remove a directory and its contents. + * + * @param string $directory + * Directory path. + * @param array $options + * Options. + */ + public static function rmdirRecursiveEmpty(string $directory, $options = []): void { if (self::dirIsEmpty($directory)) { self::rmdirRecursive($directory, $options); - self::rmdirRecursiveEmpty(dirname((string) $directory), $options); + self::rmdirRecursiveEmpty(dirname($directory), $options); } } - public static function tempdir($dir = NULL, $prefix = 'tmp_', $mode = 0700, $max_attempts = 1000): false|string { + /** + * Create a temporary directory. + * + * @param string|null $dir + * The directory where the temporary directory should be created. + * @param string $prefix + * The prefix of the directory. + * @param int $mode + * The permissions of the directory. + * @param int $max_attempts + * The maximum number of attempts to create the directory. + * + * @return false|string + * The path of the created directory or FALSE on failure. + */ + public static function tempdir(?string $dir = NULL, string $prefix = 'tmp_', int $mode = 0700, int $max_attempts = 1000): false|string { if (is_null($dir)) { $dir = sys_get_temp_dir(); } - $dir = rtrim((string) $dir, DIRECTORY_SEPARATOR); + $dir = rtrim($dir, DIRECTORY_SEPARATOR); if (!is_dir($dir) || !is_writable($dir)) { return FALSE; } - if (strpbrk((string) $prefix, '\\/:*?"<>|') !== FALSE) { + if (strpbrk($prefix, '\\/:*?"<>|') !== FALSE) { return FALSE; } $attempts = 0; @@ -89,6 +127,16 @@ public static function tempdir($dir = NULL, $prefix = 'tmp_', $mode = 0700, $max return $path; } + /** + * Replace string in filenames. + * + * @param string $search + * The value being searched for, otherwise known as the needle. + * @param string $replace + * The replacement value that replaces found search values. + * @param string $dir + * The directory where the replacement should be done. + */ public static function replaceStringFilename($search, $replace, string $dir): void { $files = self::scandirRecursive($dir, self::ignorePaths()); foreach ($files as $filename) { @@ -103,14 +151,33 @@ public static function replaceStringFilename($search, $replace, string $dir): vo } } + /** + * Check if a directory is empty. + * + * @param string $directory + * The directory path. + * + * @return bool + * Whether the directory is empty. + */ public static function dirIsEmpty($directory): bool { return is_dir($directory) && count(scandir($directory)) === 2; } /** - * @return mixed[] + * Recursively scan a directory for files. + * + * @param string $dir + * The directory path. + * @param array $ignore_paths + * The paths to ignore. + * @param bool $should_include_dirs + * Whether to include directories in the result. + * + * @return array + * The discovered files. */ - public static function scandirRecursive(string $dir, $ignore_paths = [], $include_dirs = FALSE): array { + public static function scandirRecursive(string $dir, array $ignore_paths = [], bool $should_include_dirs = FALSE): array { $discovered = []; if (is_dir($dir)) { @@ -124,10 +191,10 @@ public static function scandirRecursive(string $dir, $ignore_paths = [], $includ } } if (is_dir($path)) { - if ($include_dirs) { + if ($should_include_dirs) { $discovered[] = $path; } - $discovered = array_merge($discovered, self::scandirRecursive($path, $ignore_paths, $include_dirs)); + $discovered = array_merge($discovered, self::scandirRecursive($path, $ignore_paths, $should_include_dirs)); } else { $discovered[] = $path; @@ -138,6 +205,17 @@ public static function scandirRecursive(string $dir, $ignore_paths = [], $includ return $discovered; } + /** + * Check if a file contains a string. + * + * @param string $needle + * The value being searched for. + * @param string $file + * The file path. + * + * @return int|bool + * The number of times the needle substring occurs in the haystack string. + */ public static function fileContains($needle, $file): int|bool { if (!is_readable($file)) { return FALSE; @@ -152,6 +230,16 @@ public static function fileContains($needle, $file): int|bool { return str_contains($content, (string) $needle); } + /** + * Replace content in files. + * + * @param string $needle + * The value being searched for. + * @param string $replacement + * The replacement value that replaces found search values. + * @param string $dir + * The directory where the replacement should be done. + */ public static function dirReplaceContent($needle, $replacement, string $dir): void { $files = self::scandirRecursive($dir, self::ignorePaths()); foreach ($files as $filename) { @@ -159,6 +247,16 @@ public static function dirReplaceContent($needle, $replacement, string $dir): vo } } + /** + * Replace content in a file. + * + * @param string $needle + * The value being searched for. + * @param string $replacement + * The replacement value that replaces found search values. + * @param string $filename + * The file path. + */ public static function fileReplaceContent($needle, $replacement, $filename) { if (!is_readable($filename) || self::fileIsExcludedFromProcessing($filename)) { return FALSE; @@ -177,6 +275,14 @@ public static function fileReplaceContent($needle, $replacement, $filename) { } } + /** + * Recursively remove a directory and its contents. + * + * @param string $directory + * The directory path. + * @param array $options + * Options. + */ public static function rmdirRecursive($directory, array $options = []): void { if (!isset($options['traverseSymlinks'])) { $options['traverseSymlinks'] = FALSE; @@ -208,6 +314,17 @@ public static function rmdirRecursive($directory, array $options = []): void { } } + /** + * Check if a directory contains a string. + * + * @param string $needle + * The value being searched for. + * @param string $dir + * The directory path. + * + * @return bool + * Whether the directory contains the string. + */ public static function dirContains($needle, string $dir): bool { $files = self::scandirRecursive($dir, self::ignorePaths()); foreach ($files as $filename) { @@ -219,6 +336,17 @@ public static function dirContains($needle, string $dir): bool { return FALSE; } + /** + * Get the value of a key from composer.json. + * + * @param string $name + * The key name. + * @param string $dir + * The directory path. + * + * @return mixed|null + * The value of the key or NULL if the key does not exist. + */ public static function getComposerJsonValue($name, string $dir) { $composer_json = $dir . DIRECTORY_SEPARATOR . 'composer.json'; if (is_readable($composer_json)) { @@ -231,6 +359,17 @@ public static function getComposerJsonValue($name, string $dir) { return NULL; } + /** + * Find a matching path. + * + * @param string|array $paths + * The path or paths. + * @param string|null $text + * The text to search for. + * + * @return string|null + * The matching path or NULL if no match is found. + */ public static function findMatchingPath($paths, $text = NULL) { $paths = is_array($paths) ? $paths : [$paths]; @@ -257,6 +396,12 @@ public static function findMatchingPath($paths, $text = NULL) { return NULL; } + /** + * Ignore paths. + * + * @return array + * The paths to ignore. + */ public static function ignorePaths(): array { return array_merge([ '/.git/', @@ -267,6 +412,12 @@ public static function ignorePaths(): array { ], self::internalPaths()); } + /** + * Internal paths. + * + * @return array + * The internal paths. + */ public static function internalPaths(): array { return [ '/scripts/drevops/installer/install', @@ -277,12 +428,30 @@ public static function internalPaths(): array { ]; } - public static function isInternalPath($relative_path): bool { - $relative_path = '/' . ltrim((string) $relative_path, './'); + /** + * Check if a path is internal. + * + * @param string $relative_path + * The relative path. + * + * @return bool + * Whether the path is internal. + */ + public static function isInternalPath(string $relative_path): bool { + $relative_path = '/' . ltrim($relative_path, './'); return in_array($relative_path, Files::internalPaths()); } + /** + * Check if a file is excluded from processing. + * + * @param string $filename + * The filename. + * + * @return int|false + * The result of the match or FALSE if the file is not excluded. + */ public static function fileIsExcludedFromProcessing($filename): int|false { $excluded_patterns = [ '.+\.png', @@ -295,6 +464,17 @@ public static function fileIsExcludedFromProcessing($filename): int|false { return preg_match('/^(' . implode('|', $excluded_patterns) . ')$/', (string) $filename); } + /** + * Glob recursively. + * + * @param string $pattern + * The pattern. + * @param int $flags + * The flags. + * + * @return array|false + * The result of the glob or FALSE if no match is found. + */ public static function globRecursive($pattern, $flags = 0): array|false { $files = glob($pattern, $flags | GLOB_BRACE); foreach (glob(dirname((string) $pattern) . '/{,.}*[!.]', GLOB_BRACE | GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { @@ -304,6 +484,12 @@ public static function globRecursive($pattern, $flags = 0): array|false { return $files; } + /** + * Remove a file. + * + * @param string $file + * The file path. + */ public static function remove($file): void { @unlink($file); } diff --git a/.drevops/installer/src/Utils/Formatter.php b/.drevops/installer/src/Utils/Formatter.php index 7a134c9af..95264f3ba 100644 --- a/.drevops/installer/src/Utils/Formatter.php +++ b/.drevops/installer/src/Utils/Formatter.php @@ -7,26 +7,77 @@ use Symfony\Component\Console\Helper\TableStyle; /** - * + * Formatter. */ class Formatter { - public static function formatNotEmpty($value, $default) { + /** + * Format a value if it is not empty. + * + * @param mixed $value + * The value. + * @param mixed $default + * The default value. + * + * @return mixed + * The formatted value. + */ + public static function formatNotEmpty(mixed $value, mixed $default) { return empty($value) ? $default : $value; } - public static function formatEmpty($value) { + /** + * Format a value if it is empty. + * + * @param mixed $value + * The value. + * + * @return mixed + * The formatted value. + */ + public static function formatEmpty(mixed $value) { return empty($value) ? '(empty)' : $value; } - public static function formatYesNo($value): string { + /** + * Format a value as Yes or No. + * + * @param mixed $value + * The value. + * + * @return string + * The formatted value. + */ + public static function formatYesNo(mixed $value): string { return empty($value) ? 'No' : 'Yes'; } - public static function formatEnabled($value): string { + /** + * Format a value as Enabled or Disabled. + * + * @param mixed $value + * The value. + * + * @return string + * The formatted value. + */ + public static function formatEnabledDisabled(mixed $value): string { return empty($value) ? 'Disabled' : 'Enabled'; } + /** + * Format a value as a list of key-value pairs. + * + * @param array $values + * The values. + * @param string $delim + * The delimiter. + * @param int $width + * The width. + * + * @return string + * The formatted value. + */ public static function formatValuesList($values, $delim = '', $width = 80): string { // Line width - length of delimiters * 2 - 2 spacers. $line_width = $width - strlen((string) $delim) * 2 - 2; @@ -71,25 +122,39 @@ public static function formatValuesList($values, $delim = '', $width = 80): stri return implode('', $output); } + /** + * Print a box. + * + * @param \Symfony\Component\Console\Output\OutputInterface $output + * The output. + * @param array|string $values + * The values. + * @param string $title + * The title. + * @param string $style + * The style. + * @param int $pad_rows + * The number of padding rows. + */ public static function printBox($output, $values, $title = '', $style = 'box-double', $pad_rows = 1): void { $table = new Table($output); if (is_array($values)) { - $rows = array_map(static function ($key, $value) : TableSeparator|array { - return $value instanceof TableSeparator ? $value : [$key, $value]; + $rows = array_map(static function ($key, $value): TableSeparator|array { + return $value instanceof TableSeparator ? $value : [$key, $value]; }, array_keys($values), $values); } else { $rows = [[$values]]; } - if ($pad_rows) { + if ($pad_rows !== 0) { array_unshift($rows, array_fill(0, $pad_rows, '')); $rows[] = array_fill(0, $pad_rows, ''); } $table->setRows($rows); - if ($title) { + if ($title !== '' && $title !== '0') { $table->setHeaderTitle($title); } diff --git a/.drevops/installer/src/Utils/Git.php b/.drevops/installer/src/Utils/Git.php index 0f979f915..9d5d4ddce 100644 --- a/.drevops/installer/src/Utils/Git.php +++ b/.drevops/installer/src/Utils/Git.php @@ -3,10 +3,23 @@ namespace DrevOps\Installer\Utils; /** + * Git. * + * Git utilities. */ class Git { + /** + * Check if a file is tracked by git. + * + * @param string $path + * The path. + * @param string $dir + * The directory. + * + * @return bool + * Whether the file is tracked by git. + */ public static function fileIsTracked($path, string $dir): bool { if (is_dir($dir . DIRECTORY_SEPARATOR . '.git')) { $cwd = getcwd(); diff --git a/.drevops/installer/src/Utils/Strings.php b/.drevops/installer/src/Utils/Strings.php index c17db539c..66549891a 100644 --- a/.drevops/installer/src/Utils/Strings.php +++ b/.drevops/installer/src/Utils/Strings.php @@ -3,21 +3,32 @@ namespace DrevOps\Installer\Utils; /** - * + * String utilities. */ class Strings { - public static function toMachineName($value, $preserve_chars = []) { + /** + * Convert a string to a machine name. + * + * @param string $value + * The value. + * @param array $preserve_chars + * The characters to preserve. + * + * @return string + * The machine name. + */ + public static function toMachineName(string $value, $preserve_chars = []): string { if (empty($value)) { return $value; } // If the value doesn't start with an uppercase or lowercase letter, return as-is. - if (!preg_match('/^[a-zA-Z]/', trim((string) $value))) { + if (!preg_match('/^[a-zA-Z]/', trim($value))) { return $value; } - $value = trim((string) $value); + $value = trim($value); $preserve = ''; foreach ($preserve_chars as $char) { @@ -30,34 +41,76 @@ public static function toMachineName($value, $preserve_chars = []) { return strtolower($value); } - public static function toHumanName($value): ?string { - $value = preg_replace('/[^a-zA-Z0-9]/', ' ', (string) $value); + /** + * Convert a string to a human name. + * + * @param string $value + * The value. + * + * @return string|null + * The human name. + */ + public static function toHumanName(string $value): ?string { + $value = preg_replace('/[^a-zA-Z0-9]/', ' ', $value); $value = trim($value); return preg_replace('/\s{2,}/', ' ', $value); } - public static function toAbbreviation($value, $maxlength = 2, $word_delim = '_'): string|array { - $value = trim((string) $value); + /** + * Convert a string to an abbreviation. + * + * @param string $value + * The value. + * @param int $maxlength + * The maximum length. + * @param string $word_delim + * The word delimiter. + * + * @return string + * The abbreviation. + */ + public static function toAbbreviation(string $value, $maxlength = 2, $word_delim = '_'): string { + $value = trim($value); $value = str_replace(' ', '_', $value); $parts = explode($word_delim, $value); if (count($parts) == 1) { return strlen($parts[0]) > $maxlength ? substr($parts[0], 0, $maxlength) : $value; } - $value = implode('', array_map(static function ($word) : string { - return substr($word, 0, 1); + $value = implode('', array_map(static function ($word): string { + return substr($word, 0, 1); }, $parts)); return substr($value, 0, $maxlength); } + /** + * Convert a string to a URL. + * + * @param string $string + * The string. + * + * @return string + * The URL. + */ public static function toUrl(string $string): string { // @todo Add more replacements. return str_replace([' ', '_'], '-', $string); } - public static function listToString(mixed $value, $is_multiline = FALSE): mixed { + /** + * Convert a string to a list. + * + * @param mixed $value + * The value. + * @param bool $is_multiline + * Whether to use multiline. + * + * @return string + * The value. + */ + public static function listToString(mixed $value, $is_multiline = FALSE): string { if (is_array($value)) { $value = implode($is_multiline ? PHP_EOL : ', ', $value); } @@ -65,12 +118,21 @@ public static function listToString(mixed $value, $is_multiline = FALSE): mixed return $value; } - public static function isRegex($str): bool { + /** + * Check if a string is a regex. + * + * @param string $str + * The string. + * + * @return bool + * Whether the string is a regex. + */ + public static function isRegex(string $str): bool { // First character is always the start. $start = $str[0]; // Exclude any of the invalid starting characters. - if (preg_match('/[*?[:alnum:] \\\\]/', (string) $start)) { + if (preg_match('/[*?[:alnum:] \\\\]/', $start)) { return FALSE; } @@ -83,7 +145,7 @@ public static function isRegex($str): bool { // The regex should look something like this: /pattern/modifiers // Extract the pattern and the modifiers. - if (!preg_match('#^' . preg_quote((string) $start) . '(.*?)' . preg_quote((string) $end) . '([imsxuADU]*)$#', (string) $str, $matches)) { + if (!preg_match('#^' . preg_quote($start) . '(.*?)' . preg_quote($end) . '([imsxuADU]*)$#', $str, $matches)) { return FALSE; } diff --git a/.drevops/installer/src/Utils/Token.php b/.drevops/installer/src/Utils/Token.php index bed67210a..d44b2e6c7 100644 --- a/.drevops/installer/src/Utils/Token.php +++ b/.drevops/installer/src/Utils/Token.php @@ -40,7 +40,7 @@ class Token { /** * Get all constants defined in this class. */ - public static function getConstants():array { + public static function getConstants(): array { return ConstantsLoader::load(__CLASS__); } diff --git a/.drevops/installer/src/Utils/Tokenizer.php b/.drevops/installer/src/Utils/Tokenizer.php index fb1cc8711..f599814b1 100644 --- a/.drevops/installer/src/Utils/Tokenizer.php +++ b/.drevops/installer/src/Utils/Tokenizer.php @@ -3,10 +3,18 @@ namespace DrevOps\Installer\Utils; /** - * + * Tokenizer. */ class Tokenizer { + /** + * Remove token from directory with content. + * + * @param string $token + * The token. + * @param string $dir + * The directory. + */ public static function removeTokenWithContentFromDir(string $token, string $dir): void { self::validateToken($token); $files = Files::scandirRecursive($dir, Files::ignorePaths()); @@ -15,7 +23,15 @@ public static function removeTokenWithContentFromDir(string $token, string $dir) } } - public static function removeTokenLineFromDir($token, string $dir): void { + /** + * Remove token line from directory. + * + * @param string $token + * The token. + * @param string $dir + * The directory. + */ + public static function removeTokenLineFromDir(string $token, string $dir): void { if (!empty($token)) { self::validateToken($token); $files = Files::scandirRecursive($dir, Files::ignorePaths()); @@ -25,7 +41,19 @@ public static function removeTokenLineFromDir($token, string $dir): void { } } - public static function removeTokenFromFile($filename, $token_begin, $token_end = NULL, $with_content = FALSE): void { + /** + * Remove token from file. + * + * @param string $filename + * The filename. + * @param string|null $token_begin + * The token begin. + * @param string|null $token_end + * The token end. + * @param bool $with_content + * Whether to remove content. + */ + public static function removeTokenFromFile(string $filename, ?string $token_begin, string $token_end = NULL, bool $with_content = FALSE): void { if (Files::fileIsExcludedFromProcessing($filename)) { return; } @@ -35,12 +63,27 @@ public static function removeTokenFromFile($filename, $token_begin, $token_end = file_put_contents($filename, $newContent); } - public static function removeTokensFromString($content, $token_begin, $token_end = NULL, $with_content = FALSE): string { + /** + * Remove tokens from string. + * + * @param string $content + * The content. + * @param string $token_begin + * The token begin. + * @param string|null $token_end + * The token end. + * @param bool $with_content + * Whether to remove content. + * + * @return string + * The content. + */ + public static function removeTokensFromString(string $content, string $token_begin, ?string $token_end = NULL, bool $with_content = FALSE): string { $token_end = $token_end ?? $token_begin; - if ($token_begin != $token_end) { - $token_begin_count = preg_match_all('/' . preg_quote((string) $token_begin) . '/', (string) $content); - $token_end_count = preg_match_all('/' . preg_quote((string) $token_end) . '/', (string) $content); + if ($token_begin !== $token_end) { + $token_begin_count = preg_match_all('/' . preg_quote($token_begin) . '/', $content); + $token_end_count = preg_match_all('/' . preg_quote($token_end) . '/', $content); if ($token_begin_count !== $token_end_count) { throw new \RuntimeException(sprintf('Invalid begin and end token count: begin is %s(%s), end is %s(%s).', $token_begin, $token_begin_count, $token_end, $token_end_count)); } @@ -49,15 +92,15 @@ public static function removeTokensFromString($content, $token_begin, $token_end $out = []; $within_token = FALSE; - $lines = explode(PHP_EOL, (string) $content); + $lines = explode(PHP_EOL, $content); foreach ($lines as $line) { - if (str_contains($line, (string) $token_begin)) { + if (str_contains($line, $token_begin)) { if ($with_content) { $within_token = TRUE; } continue; } - elseif (str_contains($line, (string) $token_end)) { + elseif (str_contains($line, $token_end)) { if ($with_content) { $within_token = FALSE; } diff --git a/.drevops/installer/src/Utils/Validator.php b/.drevops/installer/src/Utils/Validator.php index 67eb52f7c..3ec3d5212 100644 --- a/.drevops/installer/src/Utils/Validator.php +++ b/.drevops/installer/src/Utils/Validator.php @@ -3,11 +3,20 @@ namespace DrevOps\Installer\Utils; /** - * + * Validator. */ class Validator { - public static function notEmpty($value): void { + /** + * Validate the value is not empty. + * + * @param mixed $value + * The value. + * + * @throws \Exception + * If the value is empty. + */ + public static function notEmpty(mixed $value): void { $value = is_array($value) ? $value : [$value]; $value = array_filter($value); @@ -16,19 +25,50 @@ public static function notEmpty($value): void { } } - public static function humanName($value): void { + /** + * Validate the value is a human name. + * + * @param mixed $value + * The value. + * + * @throws \Exception + * If the value is not a human name. + */ + public static function humanName(mixed $value): void { if (!preg_match('/^[a-zA-Z0-9\- ]+$/', (string) $value)) { throw new \Exception('The name must contain only letters, numbers, and dashes.'); } } - public static function machineName($value): void { + /** + * Validate the value is a machine name. + * + * @param mixed $value + * The value. + * + * @throws \Exception + * If the value is not a machine name. + */ + public static function machineName(mixed $value): void { if (!preg_match('/^[a-z0-9_]+$/', (string) $value)) { throw new \Exception('The name must contain only lowercase letters, numbers, and underscores.'); } } - public static function inList($items, $value, $is_multiple = FALSE): void { + /** + * Validate the value is in a list. + * + * @param array $items + * The list of items. + * @param mixed $value + * The value. + * @param bool $is_multiple + * Whether the value is a list. + * + * @throws \Exception + * If the value is not in the list. + */ + public static function inList($items, mixed $value, $is_multiple = FALSE): void { $value = is_array($value) ? $value : [$value]; if ($is_multiple) { @@ -42,14 +82,31 @@ public static function inList($items, $value, $is_multiple = FALSE): void { } } - public static function dockerImageName($value): void { + /** + * Validate the value is a valid Docker image name. + * + * @param mixed $value + * The value. + */ + public static function dockerImageName(string $value): void { $pattern = '%^(?(?<=^)(?:(?(?:(?:localhost|[\w-]+(?:\.[\w-]+)+)(?::\d+)?)|[\w]+:\d+)\/)?\/?(?(?:(?:[a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9]+)*)\/)*)(?[a-z0-9-]+))[:@]?(?(?<=:)(?[\w][\w.-]{0,127})|(?<=@)(?[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][0-9A-Fa-f]{32,}))?$%m'; - if (!preg_match($pattern, (string) $value)) { + if (!preg_match($pattern, $value)) { throw new \Exception('The name must contain only lowercase letters, numbers, dashes, and underscores.'); } } - public static function url($value, $require_protocol = FALSE): void { + /** + * Validate the value is a valid URL. + * + * @param mixed $value + * The value. + * @param bool $require_protocol + * Whether the URL must have a protocol. + * + * @throws \Exception + * If the value is not a valid URL. + */ + public static function url(mixed $value, $require_protocol = FALSE): void { if ($require_protocol === FALSE && !str_contains((string) $value, '://')) { // If the URL starts with '//' (protocol-relative), prepend with 'http:'. $value = (str_starts_with((string) $value, '//')) ? 'http:' . $value : 'http://' . $value; diff --git a/.drevops/installer/tests/phpunit/Unit/Prompt/Abstract/AbstractChoicePromptUnitTest.php b/.drevops/installer/tests/phpunit/Unit/Prompt/Abstract/AbstractChoicePromptUnitTest.php index 10aaf6bb8..d9d243b49 100644 --- a/.drevops/installer/tests/phpunit/Unit/Prompt/Abstract/AbstractChoicePromptUnitTest.php +++ b/.drevops/installer/tests/phpunit/Unit/Prompt/Abstract/AbstractChoicePromptUnitTest.php @@ -433,29 +433,42 @@ public static function dataProviderAsk(): array { } /** - * + * Erroneous choice prompt fixture. */ class ErroneousChoicePromptFixture extends AbstractChoicePrompt { - public static function choices() { + /** + * {@inheritdoc} + */ + public static function choices(): array { + return []; } } /** - * + * Filled choice prompt fixture. */ class FilledChoicePromptFixture extends AbstractChoicePrompt { + /** + * {@inheritdoc} + */ public static function title(): string { return 'Fixture title'; } + /** + * {@inheritdoc} + */ public static function question(): string { return 'Fixture question'; } - public static function choices() { + /** + * {@inheritdoc} + */ + public static function choices(): array { return [ 'choice1', 'choice2', @@ -466,10 +479,13 @@ public static function choices() { } /** - * + * Filled multi choice prompt fixture. */ class FilledMultiChoicePromptFixture extends FilledChoicePromptFixture { + /** + * {@inheritdoc} + */ protected $isMultiselect = TRUE; } diff --git a/.drevops/installer/tests/phpunit/Unit/Prompt/PromptManagerTest.php b/.drevops/installer/tests/phpunit/Unit/Prompt/PromptManagerTest.php index d0d648ac3..625e31b4a 100644 --- a/.drevops/installer/tests/phpunit/Unit/Prompt/PromptManagerTest.php +++ b/.drevops/installer/tests/phpunit/Unit/Prompt/PromptManagerTest.php @@ -45,13 +45,13 @@ public function testAskQuestions(mixed $callback, ?string $expectedException = N public static function dataProviderAskQuestions(): array { return [ 'valid callback' => [ - static function ($manager) : void { + static function ($manager): void { }, NULL, ], 'invalid callback not accessible' => [ static function () { - return (new \DrevOps\Installer\Tests\Unit\Prompt\PromptTestClassWithProtectedCallback())->callbackExample(); + return (new \DrevOps\Installer\Tests\Unit\Prompt\PromptTestClassWithProtectedCallback())->callbackExample(); }, \Error::class, ], @@ -99,11 +99,12 @@ public function testGetPromptClassInvalid(): void { } /** - * + * Test class with protected callback. */ class PromptTestClassWithProtectedCallback { - protected function callbackExample() { + protected function callbackExample(): mixed { + return TRUE; } } diff --git a/.drevops/installer/tests/phpunit/Unit/Prompt/PromptUnitTestCase.php b/.drevops/installer/tests/phpunit/Unit/Prompt/PromptUnitTestCase.php index 3d52994e9..827f2e882 100644 --- a/.drevops/installer/tests/phpunit/Unit/Prompt/PromptUnitTestCase.php +++ b/.drevops/installer/tests/phpunit/Unit/Prompt/PromptUnitTestCase.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; /** - * + * Prompt unit test case. */ abstract class PromptUnitTestCase extends UnitTestCase { diff --git a/.drevops/installer/tests/phpunit/Unit/Utils/DotEnvTest.php b/.drevops/installer/tests/phpunit/Unit/Utils/DotEnvTest.php index 4ad15acf7..1ee7aaa49 100644 --- a/.drevops/installer/tests/phpunit/Unit/Utils/DotEnvTest.php +++ b/.drevops/installer/tests/phpunit/Unit/Utils/DotEnvTest.php @@ -2,7 +2,6 @@ namespace Drevops\Installer\Tests\Unit\Utils; -use DrevOps\Installer\Command\Installer; use Drevops\Installer\Tests\Unit\UnitTestCase; use DrevOps\Installer\Utils\DotEnv; @@ -30,14 +29,14 @@ class DotEnvTest extends UnitTestCase { */ protected $backupEnv; - public function setUp(): void { + protected function setUp(): void { $this->backupEnv = $GLOBALS['_ENV']; $this->backupServer = $GLOBALS['_SERVER']; parent::setUp(); } - public function tearDown(): void { + protected function tearDown(): void { $GLOBALS['_ENV'] = $this->backupEnv; $GLOBALS['_SERVER'] = $this->backupServer; } @@ -46,7 +45,7 @@ public function tearDown(): void { * @covers \DrevOps\Installer\Utils\DotEnv::loadDotenv * @runInSeparateProcess */ - public function testLoadDotEnv() { + public function testLoadDotEnv(): void { $content = 'var1=val1'; $filename = $this->createFixtureEnvFile($content); @@ -73,8 +72,8 @@ public function testLoadDotEnv() { * @dataProvider dataProviderGlobals * @runInSeparateProcess */ - public function testGlobals($content, $env_before, $server_before, $env_after, $server_after, $allow_override) { - $filename = !empty($content) ? $this->createFixtureEnvFile($content) : 'randomfilename'; + public function testGlobals(string $content, array $env_before, array $server_before, array $env_after, mixed $server_after, bool $allow_override): void { + $filename = empty($content) ? 'randomfilename' : $this->createFixtureEnvFile($content); $GLOBALS['_ENV'] = $env_before; $GLOBALS['_SERVER'] = $server_before; @@ -88,7 +87,7 @@ public function testGlobals($content, $env_before, $server_before, $env_after, $ $this->assertTrue(TRUE); } - public static function dataProviderGlobals() { + public static function dataProviderGlobals(): array { return [ [ '', [], [], [], [], FALSE, @@ -217,7 +216,7 @@ public static function dataProviderGlobals() { ]; } - protected function createFixtureEnvFile($content) { + protected function createFixtureEnvFile($content): string|false { $filename = tempnam(sys_get_temp_dir(), '.env'); file_put_contents($filename, $content);