From e525aca251020d65d757da2e9a98e758bcd4df6e Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Wed, 18 Dec 2024 10:07:18 +0000 Subject: [PATCH] Fix some phpstan level 7 errors --- src/Command/Activity/ActivityListCommand.php | 4 +++ src/Command/App/AppConfigGetCommand.php | 2 +- src/Command/Auth/BrowserLoginCommand.php | 12 +++++---- src/Command/Auth/VerifyPhoneNumberCommand.php | 12 +++++---- src/Command/Backup/BackupCreateCommand.php | 2 +- src/Command/Backup/BackupRestoreCommand.php | 1 + .../BlueGreen/BlueGreenConcludeCommand.php | 2 +- .../BlueGreen/BlueGreenDeployCommand.php | 2 +- .../BlueGreen/BlueGreenEnableCommand.php | 2 +- src/Command/BotCommand.php | 17 +++++++++--- .../Certificate/CertificateListCommand.php | 5 ++++ src/Command/CommandBase.php | 10 ++++--- src/Command/Commit/CommitListCommand.php | 6 ++++- src/Command/Db/DbDumpCommand.php | 3 +++ src/Command/Db/DbSizeCommand.php | 27 +++++++++++++------ src/Command/DecodeCommand.php | 4 +-- src/Command/Domain/DomainAddCommand.php | 4 +-- src/Command/Domain/DomainCommandBase.php | 6 ++--- src/Command/Domain/DomainListCommand.php | 2 +- .../EnvironmentActivateCommand.php | 4 +++ .../Environment/EnvironmentBranchCommand.php | 5 +++- .../EnvironmentCheckoutCommand.php | 4 +-- .../Environment/EnvironmentDeleteCommand.php | 4 +++ .../EnvironmentHttpAccessCommand.php | 4 +-- .../Environment/EnvironmentInfoCommand.php | 2 +- .../Environment/EnvironmentListCommand.php | 9 ++++++- .../Environment/EnvironmentMergeCommand.php | 1 + .../Environment/EnvironmentPushCommand.php | 3 ++- .../EnvironmentSetRemoteCommand.php | 15 ++++++----- .../Environment/EnvironmentUrlCommand.php | 6 ++--- src/Command/HasExamplesTrait.php | 5 +++- .../Integration/IntegrationCommandBase.php | 2 +- src/Command/Project/ProjectGetCommand.php | 4 +-- .../Project/ProjectSetRemoteCommand.php | 2 +- src/Command/Team/TeamListCommand.php | 2 +- src/Command/Version/VersionListCommand.php | 2 +- src/Command/WebCommand.php | 2 +- src/Local/LocalProject.php | 10 +++---- src/Service/Identifier.php | 4 +-- src/Util/SslUtil.php | 5 ++-- 40 files changed, 145 insertions(+), 73 deletions(-) diff --git a/src/Command/Activity/ActivityListCommand.php b/src/Command/Activity/ActivityListCommand.php index 400400fd10..97f28f7a90 100644 --- a/src/Command/Activity/ActivityListCommand.php +++ b/src/Command/Activity/ActivityListCommand.php @@ -11,6 +11,7 @@ use Platformsh\Cli\Service\ActivityMonitor; use Platformsh\Cli\Service\PropertyFormatter; use Platformsh\Cli\Service\Table; +use Platformsh\Client\Model\Activity; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -183,6 +184,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } + /** + * @param Activity[] $activities + */ private function suggestExclusions(array $activities): void { $counts = []; diff --git a/src/Command/App/AppConfigGetCommand.php b/src/Command/App/AppConfigGetCommand.php index 888f8510d6..1fe7b487d2 100644 --- a/src/Command/App/AppConfigGetCommand.php +++ b/src/Command/App/AppConfigGetCommand.php @@ -43,7 +43,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $prefix = $this->config->getStr('service.env_prefix'); if (getenv($prefix . 'APPLICATION') && !LocalHost::conflictsWithCommandLineOptions($input, $prefix)) { $this->io->debug('Reading application config from environment variable ' . $prefix . 'APPLICATION'); - $decoded = json_decode(base64_decode(getenv($prefix . 'APPLICATION'), true), true); + $decoded = json_decode((string) base64_decode(getenv($prefix . 'APPLICATION'), true), true); if (!is_array($decoded)) { throw new \RuntimeException('Failed to decode: ' . $prefix . 'APPLICATION'); } diff --git a/src/Command/Auth/BrowserLoginCommand.php b/src/Command/Auth/BrowserLoginCommand.php index 76a51f4fad..2a73261180 100644 --- a/src/Command/Auth/BrowserLoginCommand.php +++ b/src/Command/Auth/BrowserLoginCommand.php @@ -191,7 +191,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->stdErr->writeln(''); // Wait for the file to be filled with an OAuth2 authorization code. - /** @var array|null $response */ + /** @var null|array{code: string, redirect_uri: string}|array{error: string, error_description: string, error_hint: string} $response */ $response = null; $start = time(); while ($process->isRunning()) { @@ -262,7 +262,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (empty($token['refresh_token'])) { $this->stdErr->writeln(''); - $clientId = $this->config->get('api.oauth2_client_id'); + $clientId = $this->config->getStr('api.oauth2_client_id'); $this->stdErr->writeln([ 'Warning:', 'No refresh token is available. This will cause frequent login errors.', @@ -275,7 +275,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param array $tokenData + * @param array $tokenData * @param SessionInterface $session */ private function saveAccessToken(array $tokenData, SessionInterface $session): void @@ -306,11 +306,13 @@ private function createDocumentRoot(string $dir): void /** * Exchanges the authorization code for an access token. + * + * @return array */ private function getAccessToken(string $authCode, string $codeVerifier, string $redirectUri): array { $client = new Client(['verify' => !$this->config->getWithDefault('api.skip_ssl', false)]); - $request = new Request('POST', $this->config->get('api.oauth2_token_url'), body: http_build_query([ + $request = new Request('POST', $this->config->getStr('api.oauth2_token_url'), body: http_build_query([ 'grant_type' => 'authorization_code', 'code' => $authCode, 'redirect_uri' => $redirectUri, @@ -325,7 +327,7 @@ private function getAccessToken(string $authCode, string $codeVerifier, string $ 'auth' => [$this->config->get('api.oauth2_client_id'), ''], ]); - return Utils::jsonDecode((string) $response->getBody(), true); + return (array) Utils::jsonDecode((string) $response->getBody(), true); } catch (BadResponseException $e) { throw ApiResponseException::create($request, $e->getResponse(), $e); } diff --git a/src/Command/Auth/VerifyPhoneNumberCommand.php b/src/Command/Auth/VerifyPhoneNumberCommand.php index 7521aa6252..641125c294 100644 --- a/src/Command/Auth/VerifyPhoneNumberCommand.php +++ b/src/Command/Auth/VerifyPhoneNumberCommand.php @@ -74,7 +74,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'phone_number' => $number, ], ]); - $sid = Utils::jsonDecode((string) $response->getBody(), true)['sid']; + /** @var array{sid: string} $data */ + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); + $sid = $data['sid']; if ($channel === 'call') { $this->stdErr->writeln('Calling the number ' . $number . ' with a verification code.'); @@ -86,17 +88,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->stdErr->writeln(''); - $this->questionHelper->askInput('Please enter the verification code', null, [], function ($code) use ($httpClient, $sid , $myUser): void { + $this->questionHelper->askInput('Please enter the verification code', null, [], function ($code) use ($httpClient, $sid, $myUser): void { if (!is_numeric($code)) { throw new InvalidArgumentException('Invalid verification code'); } try { - $httpClient->post('/users/' . rawurlencode($myUser->id) . '/phonenumber/' . rawurlencode((string) $sid), [ + $httpClient->post('/users/' . rawurlencode($myUser->id) . '/phonenumber/' . rawurlencode($sid), [ 'json' => ['code' => $code], ]); } catch (BadResponseException $e) { if (($response = $e->getResponse()) && $response->getStatusCode() === 400) { - $detail = Utils::jsonDecode((string) $response->getBody(), true); + $detail = (array) Utils::jsonDecode((string) $response->getBody(), true); throw new InvalidArgumentException(isset($detail['error']) ? ucfirst((string) $detail['error']) : 'Invalid verification code'); } throw $e; @@ -105,7 +107,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->io->debug('Refreshing phone verification status'); $response = $httpClient->post( '/me/verification?force_refresh=1'); - $needsVerify = Utils::jsonDecode((string) $response->getBody(), true); + $needsVerify = (array) Utils::jsonDecode((string) $response->getBody(), true); $this->stdErr->writeln(''); if ($needsVerify['type'] === 'phone') { diff --git a/src/Command/Backup/BackupCreateCommand.php b/src/Command/Backup/BackupCreateCommand.php index 0084e8104b..d04c199327 100644 --- a/src/Command/Backup/BackupCreateCommand.php +++ b/src/Command/Backup/BackupCreateCommand.php @@ -64,7 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->stdErr->writeln('The environment is not active.'); } else { try { - if ($this->isUserAdmin($selection->getProject(), $selectedEnvironment, $this->api->getMyUserId())) { + if ($this->isUserAdmin($selection->getProject(), $selectedEnvironment, (string) $this->api->getMyUserId())) { $this->stdErr->writeln('You must be an administrator to create a backup.'); } } catch (\Exception $e) { diff --git a/src/Command/Backup/BackupRestoreCommand.php b/src/Command/Backup/BackupRestoreCommand.php index d434284f85..d2d3867f6a 100644 --- a/src/Command/Backup/BackupRestoreCommand.php +++ b/src/Command/Backup/BackupRestoreCommand.php @@ -20,6 +20,7 @@ #[AsCommand(name: 'backup:restore', description: 'Restore an environment backup')] class BackupRestoreCommand extends CommandBase { + /** @var string[] */ private array $validResourcesInitOptions = ['backup', 'parent', 'default', 'minimum']; public function __construct(private readonly ActivityMonitor $activityMonitor, private readonly Api $api, private readonly Config $config, private readonly Io $io, private readonly PropertyFormatter $propertyFormatter, private readonly QuestionHelper $questionHelper, private readonly ResourcesUtil $resourcesUtil, private readonly Selector $selector) diff --git a/src/Command/BlueGreen/BlueGreenConcludeCommand.php b/src/Command/BlueGreen/BlueGreenConcludeCommand.php index c3653f35e8..e6fd4691df 100644 --- a/src/Command/BlueGreen/BlueGreenConcludeCommand.php +++ b/src/Command/BlueGreen/BlueGreenConcludeCommand.php @@ -38,7 +38,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $httpClient = $this->api->getHttpClient(); $response = $httpClient->get($environment->getLink('#versions')); - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); if (count($data) < 2) { $this->stdErr->writeln(sprintf('Blue/green deployments are not enabled for the environment %s.', $this->api->getEnvironmentLabel($environment, 'error'))); return 1; diff --git a/src/Command/BlueGreen/BlueGreenDeployCommand.php b/src/Command/BlueGreen/BlueGreenDeployCommand.php index 8b0b53133d..24558add2c 100644 --- a/src/Command/BlueGreen/BlueGreenDeployCommand.php +++ b/src/Command/BlueGreen/BlueGreenDeployCommand.php @@ -40,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $httpClient = $this->api->getHttpClient(); $response = $httpClient->get($environment->getLink('#versions')); - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); if (count($data) < 2) { $this->stdErr->writeln(sprintf('Blue/green deployments are not enabled for the environment %s.', $this->api->getEnvironmentLabel($environment, 'error'))); $this->stdErr->writeln(sprintf('Enable blue/green first by running: %s blue-green:enable', $this->config->getStr('application.executable'))); diff --git a/src/Command/BlueGreen/BlueGreenEnableCommand.php b/src/Command/BlueGreen/BlueGreenEnableCommand.php index 26ff38cb1a..565d3350aa 100644 --- a/src/Command/BlueGreen/BlueGreenEnableCommand.php +++ b/src/Command/BlueGreen/BlueGreenEnableCommand.php @@ -45,7 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $httpClient = $this->api->getHttpClient(); $response = $httpClient->get($environment->getLink('#versions')); - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); if (count($data) > 1) { $this->stdErr->writeln(sprintf('Blue/green deployments are already enabled for the environment %s.', $this->api->getEnvironmentLabel($environment))); $this->stdErr->writeln(sprintf('List versions by running: %s versions', $this->config->getStr('application.executable'))); diff --git a/src/Command/BotCommand.php b/src/Command/BotCommand.php index 3fb8e82700..0ac14d25d7 100644 --- a/src/Command/BotCommand.php +++ b/src/Command/BotCommand.php @@ -39,10 +39,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int $signature = ''; } + $files = scandir($dir); + if (!$files) { + throw new \RuntimeException('Failed to read directory: ' . $dir); + } + $frames = []; - foreach (scandir($dir) as $filename) { + foreach ($files as $filename) { if ($filename[0] !== '.') { - $frames[] = file_get_contents($dir . '/' . $filename); + $frames[] = (string) file_get_contents($dir . '/' . $filename); } } @@ -75,6 +80,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } + /** + * @param array $frames + * @param string $signature + * @return string[] + */ private function addSignature(array $frames, string $signature): array { $indent = ' '; @@ -87,7 +97,8 @@ private function addSignature(array $frames, string $signature): array } /** - * @return non-falsy-string[] + * @param string[] $frames + * @return string[] */ private function addColor(array $frames): array { diff --git a/src/Command/Certificate/CertificateListCommand.php b/src/Command/Certificate/CertificateListCommand.php index aa936911c6..59b7d0b0e0 100644 --- a/src/Command/Certificate/CertificateListCommand.php +++ b/src/Command/Certificate/CertificateListCommand.php @@ -118,6 +118,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } + /** + * @param Certificate[] $certs + * @param array $filters + * @return void + */ protected function filterCerts(array &$certs, array $filters): void { foreach ($filters as $filter => $value) { diff --git a/src/Command/CommandBase.php b/src/Command/CommandBase.php index 455e2bc2a6..0622804ba5 100644 --- a/src/Command/CommandBase.php +++ b/src/Command/CommandBase.php @@ -43,12 +43,14 @@ abstract class CommandBase extends Command implements MultiAwareInterface protected bool $runningViaMulti = false; /** + * @var string[] * @see self::setHiddenAliases() */ private array $hiddenAliases = []; /** * The command synopsis. + * @var array */ private array $synopsis = []; @@ -101,8 +103,10 @@ protected function interact(InputInterface $input, OutputInterface $output): voi /** * Adds a hidden command option. + * + * @param int-mask-of|null $mode */ - protected function addHiddenOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null): static + protected function addHiddenOption(string $name, string|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null): static { $this->getDefinition()->addOption(new HiddenInputOption($name, $shortcut, $mode, $description, $default)); @@ -114,7 +118,7 @@ protected function addHiddenOption(string $name, string|array|null $shortcut = n * * @see parent::setAliases() * - * @param array $hiddenAliases + * @param string[] $hiddenAliases * * @return static */ @@ -129,7 +133,7 @@ protected function setHiddenAliases(array $hiddenAliases): static /** * Get aliases that should be visible in help. * - * @return array + * @return string[] */ public function getVisibleAliases(): array { diff --git a/src/Command/Commit/CommitListCommand.php b/src/Command/Commit/CommitListCommand.php index c7a31b1621..29d31b9860 100644 --- a/src/Command/Commit/CommitListCommand.php +++ b/src/Command/Commit/CommitListCommand.php @@ -119,7 +119,11 @@ private function loadCommitList(Environment $environment, Commit $startCommit, i count($currentCommit->parents) && count($commits) < $limit;) { foreach (array_reverse($currentCommit->parents) as $parentSha) { if (!isset($commits[$parentSha])) { - $commits[$parentSha] = $this->gitDataApi->getCommit($environment, $parentSha); + $commit = $this->gitDataApi->getCommit($environment, $parentSha); + if (!$commit) { + throw new \RuntimeException(sprintf('Commit not found: %s', $parentSha)); + } + $commits[$parentSha] = $commit; } $currentCommit = $commits[$parentSha]; $progress->advance(); diff --git a/src/Command/Db/DbDumpCommand.php b/src/Command/Db/DbDumpCommand.php index 21b83a663e..b311dd1777 100644 --- a/src/Command/Db/DbDumpCommand.php +++ b/src/Command/Db/DbDumpCommand.php @@ -295,6 +295,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * Generates the default dump filename. + * + * @param string[] $includedTables + * @param string[] $excludedTables */ private function getDefaultFilename( ?Environment $environment = null, diff --git a/src/Command/Db/DbSizeCommand.php b/src/Command/Db/DbSizeCommand.php index 84c887a687..7912faab9e 100644 --- a/src/Command/Db/DbSizeCommand.php +++ b/src/Command/Db/DbSizeCommand.php @@ -117,11 +117,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * Returns a list of cleanup queries for a list of tables. * - * @param array $rows + * @param string[] $rows * * @see DbSizeCommand::checkInnoDbTablesInNeedOfOptimizing() * - * @return array + * @return string[] */ private function getCleanupQueries(array $rows): array { return array_filter( @@ -138,6 +138,8 @@ private function getCleanupQueries(array $rows): array { /** * Displays a list of InnoDB tables that can be usefully cleaned up. + * + * @param array $database */ private function checkInnoDbTablesInNeedOfOptimizing(HostInterface $host, array $database, InputInterface $input): void { $tablesNeedingCleanup = $host->runCommand($this->getMysqlCommand($database), true, true, $this->mysqlTablesInNeedOfOptimizing()); @@ -177,7 +179,7 @@ private function checkInnoDbTablesInNeedOfOptimizing(HostInterface $host, array * Shows a warning about schemas not accessible through this relationship. * * @param Service $service - * @param array $database + * @param array $database * * @return void */ @@ -245,7 +247,7 @@ private function psqlQuery(): string /** * Returns the psql CLI client command. * - * @param array $database + * @param array $database * * @return string */ @@ -258,6 +260,10 @@ private function getPsqlCommand(array $database): string { ); } + /** + * @param array $database + * @return string + */ private function getMongoDbCommand(array $database): string { $dbUrl = $this->relationships->getDbCommandArgs('mongo', $database); @@ -272,7 +278,7 @@ private function getMongoDbCommand(array $database): string { /** * Returns the mysql CLI client command. * - * @param array $database + * @param array $database * * @return string */ @@ -341,7 +347,7 @@ private function mysqlTablesInNeedOfOptimizing(): string { * Estimates usage of a database. * * @param HostInterface $host - * @param array $database + * @param array $database * * @return float Estimated usage in bytes. */ @@ -357,7 +363,7 @@ private function getEstimatedUsage(HostInterface $host, array $database): float * Estimates usage of a PostgreSQL database. * * @param HostInterface $host - * @param array $database + * @param array $database * * @return float Estimated usage in bytes */ @@ -365,6 +371,11 @@ private function getPgSqlUsage(HostInterface $host, array $database): float { return (float) $host->runCommand($this->getPsqlCommand($database), true, true, $this->psqlQuery()); } + /** + * @param HostInterface $host + * @param array $database + * @return float + */ private function getMongoDbUsage(HostInterface $host, array $database): float { return (float) $host->runCommand($this->getMongoDbCommand($database)); } @@ -373,7 +384,7 @@ private function getMongoDbUsage(HostInterface $host, array $database): float { * Estimates usage of a MySQL database. * * @param HostInterface $host - * @param array $database + * @param array $database * * @return float Estimated usage in bytes */ diff --git a/src/Command/DecodeCommand.php b/src/Command/DecodeCommand.php index eeac2643f7..edd29726de 100644 --- a/src/Command/DecodeCommand.php +++ b/src/Command/DecodeCommand.php @@ -13,11 +13,11 @@ #[AsCommand(name: 'decode', description: 'Decode a string that was encoded with JSON and Base64')] class DecodeCommand extends CommandBase { - public function __construct(private readonly Config $config) { parent::__construct(); } + protected function configure(): void { $envPrefix = $this->config->getStr('service.env_prefix'); @@ -90,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (is_string($value)) { $output->writeln($value); } else { - $output->writeln(json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + $output->writeln((string) json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } return 0; diff --git a/src/Command/Domain/DomainAddCommand.php b/src/Command/Domain/DomainAddCommand.php index a9cf73824e..67404bd51c 100644 --- a/src/Command/Domain/DomainAddCommand.php +++ b/src/Command/Domain/DomainAddCommand.php @@ -79,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } catch (ClientException $e) { $code = $e->getResponse()->getStatusCode(); if ($code === 402) { - $data = Utils::jsonDecode((string) $e->getResponse()->getBody(), true); + $data = (array) Utils::jsonDecode((string) $e->getResponse()->getBody(), true); if (isset($data['message'], $data['detail']['environments_with_domains_limit'], $data['detail']['environments_with_domains'])) { $this->stdErr->writeln(''); $this->stdErr->writeln($data['message']); @@ -90,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } if ($code === 409) { - $data = Utils::jsonDecode((string) $e->getResponse()->getBody(), true); + $data = (array) Utils::jsonDecode((string) $e->getResponse()->getBody(), true); if (isset($data['message'], $data['detail']['conflicting_domain']) && str_contains((string) $data['message'], 'already has a domain with the same replacement_for')) { $this->stdErr->writeln(''); $this->stdErr->writeln(sprintf( diff --git a/src/Command/Domain/DomainCommandBase.php b/src/Command/Domain/DomainCommandBase.php index 55e69f1d22..846d9f61d1 100644 --- a/src/Command/Domain/DomainCommandBase.php +++ b/src/Command/Domain/DomainCommandBase.php @@ -26,6 +26,7 @@ abstract class DomainCommandBase extends CommandBase private Api $api; // The final array of SSL options for the client parameters. + /** @var array{certificate?: string, key?: string, chain?: string[]} */ protected array $sslOptions = []; protected ?string $domainName = null; @@ -146,12 +147,11 @@ protected function validateDomainInput(InputInterface $input, Selection $selecti $choices[$productionDomain->name] = $productionDomain->name; } } - $questionHelper = $this->questionHelper; $questionText = 'Attachment (--attach)' . "\nA non-production domain must be attached to an existing production domain." . "\nIt will inherit the same routing behavior." . "\nChoose a production domain:"; - $this->attach = $questionHelper->choose($choices, $questionText, $default); + $this->attach = (string) $this->questionHelper->choose($choices, $questionText, $default); } } elseif ($this->attach !== null) { try { @@ -215,7 +215,7 @@ protected function handleApiException(ClientException $e, Project $project): voi } // @todo standardize API error parsing if the format is ever formalized if ($response->getStatusCode() === 400) { - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); if (isset($data['detail']['error'])) { $this->stdErr->writeln($data['detail']['error']); return; diff --git a/src/Command/Domain/DomainListCommand.php b/src/Command/Domain/DomainListCommand.php index 6d4e2ec0a8..74daa2ae9f 100644 --- a/src/Command/Domain/DomainListCommand.php +++ b/src/Command/Domain/DomainListCommand.php @@ -48,7 +48,7 @@ protected function configure(): void * * @param Domain[]|EnvironmentDomain[] $tree * - * @return array + * @return array> */ protected function buildDomainRows(array $tree): array { diff --git a/src/Command/Environment/EnvironmentActivateCommand.php b/src/Command/Environment/EnvironmentActivateCommand.php index cb58465c1b..29261e0565 100644 --- a/src/Command/Environment/EnvironmentActivateCommand.php +++ b/src/Command/Environment/EnvironmentActivateCommand.php @@ -21,6 +21,7 @@ #[AsCommand(name: 'environment:activate', description: 'Activate an environment')] class EnvironmentActivateCommand extends CommandBase { + /** @var string[] */ private array $validResourcesInitOptions = ['parent', 'default', 'minimum']; public function __construct( @@ -69,6 +70,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int return $success ? 0 : 1; } + /** + * @param Environment[] $environments + */ protected function activateMultiple(array $environments, Project $project, InputInterface $input, OutputInterface $output): bool { $parentId = $input->getOption('parent'); diff --git a/src/Command/Environment/EnvironmentBranchCommand.php b/src/Command/Environment/EnvironmentBranchCommand.php index f414531f27..4b76633460 100644 --- a/src/Command/Environment/EnvironmentBranchCommand.php +++ b/src/Command/Environment/EnvironmentBranchCommand.php @@ -22,6 +22,7 @@ #[AsCommand(name: 'environment:branch', description: 'Branch an environment', aliases: ['branch'])] class EnvironmentBranchCommand extends CommandBase { + /** @var string[] */ private array $validResourcesInitOptions = ['parent', 'default', 'minimum']; public function __construct(private readonly ActivityMonitor $activityMonitor, @@ -200,6 +201,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $createdNew = false; if ($checkoutLocally) { + /** @var string $projectRoot */ if ($this->git->branchExists($branchName, $projectRoot)) { $this->stdErr->writeln("Checking out $branchName locally"); if (!$this->git->checkOut($branchName, $projectRoot)) { @@ -231,7 +233,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int // project's Git URL. if ($remoteSuccess && $checkoutLocally && $createdNew) { $gitUrl = $selectedProject->getGitUrl(); - $remoteName = $this->config->get('detection.git_remote_name'); + $remoteName = $this->config->getStr('detection.git_remote_name'); + /** @var string $projectRoot */ if ($gitUrl && $this->git->getConfig(sprintf('remote.%s.url', $remoteName), $projectRoot) === $gitUrl) { $this->stdErr->writeln(sprintf( 'Setting the upstream for the local branch to: %s/%s', diff --git a/src/Command/Environment/EnvironmentCheckoutCommand.php b/src/Command/Environment/EnvironmentCheckoutCommand.php index 1c204c909b..222a7461cf 100644 --- a/src/Command/Environment/EnvironmentCheckoutCommand.php +++ b/src/Command/Environment/EnvironmentCheckoutCommand.php @@ -87,14 +87,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Determine the correct upstream for the new branch. If there is an // 'origin' remote, then it has priority. - $upstreamRemote = $this->config->get('detection.git_remote_name'); + $upstreamRemote = $this->config->getStr('detection.git_remote_name'); $originRemoteUrl = $this->git->getConfig('remote.origin.url'); if ($originRemoteUrl !== $project->getGitUrl() && $this->git->remoteBranchExists('origin', $branch)) { $upstreamRemote = 'origin'; } // Fetch the branch from the upstream remote. - $this->git->fetch($upstreamRemote, $branch, $originRemoteUrl); + $this->git->fetch($upstreamRemote, $branch, $originRemoteUrl ?: ''); $upstream = $upstreamRemote . '/' . $branch; diff --git a/src/Command/Environment/EnvironmentDeleteCommand.php b/src/Command/Environment/EnvironmentDeleteCommand.php index e43459ec80..9750aa547d 100644 --- a/src/Command/Environment/EnvironmentDeleteCommand.php +++ b/src/Command/Environment/EnvironmentDeleteCommand.php @@ -422,6 +422,10 @@ private function listEnvironments(array $environments, string $tag = 'info'): st return "<$tag>" . implode(", <$tag>", $uniqueIds) . ""; } + /** + * @param Environment[] $toDeactivate + * @param Environment[] $toDeleteBranch + */ protected function deleteMultiple(array $toDeactivate, array $toDeleteBranch, Project $project, InputInterface $input): bool { $error = false; diff --git a/src/Command/Environment/EnvironmentHttpAccessCommand.php b/src/Command/Environment/EnvironmentHttpAccessCommand.php index c1acfd67b2..afc8858067 100644 --- a/src/Command/Environment/EnvironmentHttpAccessCommand.php +++ b/src/Command/Environment/EnvironmentHttpAccessCommand.php @@ -139,12 +139,12 @@ protected function validateAddress(string $address): void } $extractIp = preg_match('#^([^/]+)(/([0-9]{1,3}))?$#', $address, $matches); $is_valid_ip = $extractIp && filter_var($matches[1], FILTER_VALIDATE_IP); - $is_valid_ipv4 = $is_valid_ip && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); - $is_valid_ipv6 = $is_valid_ip && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); if (!$extractIp || !$is_valid_ip) { $message = sprintf('The address "%s" is not a valid IP address or CIDR', $address); throw new InvalidArgumentException($message); } + $is_valid_ipv4 = filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); + $is_valid_ipv6 = filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); if ($is_valid_ipv4 && isset($matches[3]) && $matches[3] > 32) { $message = sprintf('The address "%s" is not a valid IPv4 address or CIDR', $address); throw new InvalidArgumentException($message); diff --git a/src/Command/Environment/EnvironmentInfoCommand.php b/src/Command/Environment/EnvironmentInfoCommand.php index eea3b64882..cf3b665b5f 100644 --- a/src/Command/Environment/EnvironmentInfoCommand.php +++ b/src/Command/Environment/EnvironmentInfoCommand.php @@ -110,7 +110,7 @@ protected function setProperty(string $property, string $value, Environment $env if ($property === 'parent' && $value === '-') { $value = null; } else { - settype($value, $type); + settype($value, (string) $type); } $currentValue = $environment->getProperty($property, false); diff --git a/src/Command/Environment/EnvironmentListCommand.php b/src/Command/Environment/EnvironmentListCommand.php index b4db762a8b..cea1b6457c 100644 --- a/src/Command/Environment/EnvironmentListCommand.php +++ b/src/Command/Environment/EnvironmentListCommand.php @@ -26,7 +26,11 @@ class EnvironmentListCommand extends CommandBase private array $defaultColumns = ['id', 'title', 'status', 'type']; private Environment|false $currentEnvironment = false; + + /** @var array */ private array $children = []; + + /** @var array */ private array $mapping = []; public function __construct( @@ -88,6 +92,9 @@ protected function buildEnvironmentTree(array $environments, ?string $parent = n /** * Recursively builds rows of the environment table. + * + * @param Environment[] $tree + * @return array> */ protected function buildEnvironmentRows(array $tree, bool $indent = true, bool $indicateCurrent = true, int $indentAmount = 0): array { @@ -204,7 +211,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->currentEnvironment = $this->selector->getCurrentEnvironment($project); if (($currentProject = $this->selector->getCurrentProject()) && $currentProject->id === $project->id) { - $projectConfig = $this->localProject->getProjectConfig($this->selector->getProjectRoot()); + $projectConfig = $this->localProject->getProjectConfig((string) $this->selector->getProjectRoot()); if (isset($projectConfig['mapping'])) { $this->mapping = $projectConfig['mapping']; } diff --git a/src/Command/Environment/EnvironmentMergeCommand.php b/src/Command/Environment/EnvironmentMergeCommand.php index b491d077da..aaa96b8c36 100644 --- a/src/Command/Environment/EnvironmentMergeCommand.php +++ b/src/Command/Environment/EnvironmentMergeCommand.php @@ -17,6 +17,7 @@ #[AsCommand(name: 'environment:merge', description: 'Merge an environment', aliases: ['merge'])] class EnvironmentMergeCommand extends CommandBase { + /** @var string[] */ private array $validResourcesInitOptions = ['child', 'default', 'minimum', 'manual']; public function __construct(private readonly ActivityMonitor $activityMonitor, private readonly Api $api, private readonly Config $config, private readonly QuestionHelper $questionHelper, private readonly ResourcesUtil $resourcesUtil, private readonly Selector $selector) diff --git a/src/Command/Environment/EnvironmentPushCommand.php b/src/Command/Environment/EnvironmentPushCommand.php index ebcfffdc1a..714082e839 100644 --- a/src/Command/Environment/EnvironmentPushCommand.php +++ b/src/Command/Environment/EnvironmentPushCommand.php @@ -31,6 +31,7 @@ class EnvironmentPushCommand extends CommandBase { const PUSH_FAILURE_EXIT_CODE = 87; + /** @var string[] */ private array $validResourcesInitOptions = ['parent', 'default', 'minimum', 'manual']; public function __construct(private readonly ActivityMonitor $activityMonitor, @@ -213,7 +214,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $localProject = $this->localProject; - $remoteName = $this->config->get('detection.git_remote_name'); + $remoteName = $this->config->getStr('detection.git_remote_name'); // Map the current directory to the project. if ($input->getOption('set-upstream') && (!$currentProject || $currentProject->id !== $project->id)) { diff --git a/src/Command/Environment/EnvironmentSetRemoteCommand.php b/src/Command/Environment/EnvironmentSetRemoteCommand.php index 6b805d39ec..68fa1c6513 100644 --- a/src/Command/Environment/EnvironmentSetRemoteCommand.php +++ b/src/Command/Environment/EnvironmentSetRemoteCommand.php @@ -45,14 +45,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int throw new RootNotFoundException(); } - $projectRoot = $this->selector->getProjectRoot(); + $projectRoot = (string) $this->selector->getProjectRoot(); $this->git->setDefaultRepositoryDir($projectRoot); $specifiedEnvironmentId = $input->getArgument('environment'); - if ($specifiedEnvironmentId != '0' - && !($specifiedEnvironment = $this->api->getEnvironment($specifiedEnvironmentId, $project))) { - $this->stdErr->writeln("Environment not found: $specifiedEnvironmentId"); - return 1; + $specifiedEnvironment = null; + if ($specifiedEnvironmentId != '0') { + $specifiedEnvironment = $this->api->getEnvironment($specifiedEnvironmentId, $project); + if (!$specifiedEnvironment) { + $this->stdErr->writeln("Environment not found: $specifiedEnvironmentId"); + return 1; + } } $specifiedBranch = $input->getArgument('branch'); @@ -67,7 +70,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Check whether the branch is mapped by default (its name or its Git // upstream is the same as the remote environment ID). - $mappedByDefault = isset($specifiedEnvironment) + $mappedByDefault = $specifiedEnvironment && $specifiedEnvironment->status != 'inactive' && $specifiedEnvironmentId === $specifiedBranch; if ($specifiedEnvironmentId != '0' && !$mappedByDefault) { diff --git a/src/Command/Environment/EnvironmentUrlCommand.php b/src/Command/Environment/EnvironmentUrlCommand.php index 9c3b21ea2f..9077d8b836 100644 --- a/src/Command/Environment/EnvironmentUrlCommand.php +++ b/src/Command/Environment/EnvironmentUrlCommand.php @@ -44,7 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $prefix = $this->config->getStr('service.env_prefix'); if (getenv($prefix . 'ROUTES') && !LocalHost::conflictsWithCommandLineOptions($input, $prefix)) { $this->io->debug('Reading URLs from environment variable ' . $prefix . 'ROUTES'); - $decoded = json_decode(base64_decode(getenv($prefix . 'ROUTES'), true), true); + $decoded = json_decode((string) base64_decode(getenv($prefix . 'ROUTES'), true), true); if (empty($decoded)) { throw new \RuntimeException('Failed to decode: ' . $prefix . 'ROUTES'); } @@ -87,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * Displays or opens URLs. * - * @param string[] $urls + * @param string[] $urls * @param InputInterface $input * @param OutputInterface $output */ @@ -121,7 +121,7 @@ private function displayOrOpenUrls(array $urls, InputInterface $input, OutputInt $url = $urls[0]; } else { $questionHelper = $this->questionHelper; - $url = $questionHelper->choose(array_combine($urls, $urls), 'Enter a number to open a URL', $urls[0]); + $url = (string) $questionHelper->choose(array_combine($urls, $urls), 'Enter a number to open a URL', $urls[0]); } $this->url->openUrl($url); diff --git a/src/Command/HasExamplesTrait.php b/src/Command/HasExamplesTrait.php index 9709fdd137..73f4782003 100644 --- a/src/Command/HasExamplesTrait.php +++ b/src/Command/HasExamplesTrait.php @@ -4,7 +4,7 @@ trait HasExamplesTrait { - /** @var array{'commandline': string, 'description': string}[] */ + /** @var array */ private array $examples = []; protected function addExample(string $description, string $commandline = ''): self @@ -14,6 +14,9 @@ protected function addExample(string $description, string $commandline = ''): se return $this; } + /** + * @return array + */ public function getExamples(): array { return $this->examples; diff --git a/src/Command/Integration/IntegrationCommandBase.php b/src/Command/Integration/IntegrationCommandBase.php index 6873598ea6..aeac16acc2 100644 --- a/src/Command/Integration/IntegrationCommandBase.php +++ b/src/Command/Integration/IntegrationCommandBase.php @@ -673,7 +673,7 @@ protected function getBitbucketAccessToken(array $credentials): string ], ]); - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); if (!isset($data['access_token'])) { throw new \RuntimeException('Access token not found in Bitbucket response'); } diff --git a/src/Command/Project/ProjectGetCommand.php b/src/Command/Project/ProjectGetCommand.php index ad97ec7f81..d8f6532e37 100644 --- a/src/Command/Project/ProjectGetCommand.php +++ b/src/Command/Project/ProjectGetCommand.php @@ -166,7 +166,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'Commit and push to the %s branch of the %s Git remote' . ', and %s will build your project automatically.', $project->default_branch, - $this->config->get('detection.git_remote_name'), + $this->config->getStr('detection.git_remote_name'), $this->config->getStr('service.name') )); @@ -179,7 +179,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int '--branch', $environment->id, '--origin', - $this->config->get('detection.git_remote_name'), + $this->config->getStr('detection.git_remote_name'), ]; if ($this->stdErr->isDecorated() && $this->io->isTerminal(STDERR)) { $cloneArgs[] = '--progress'; diff --git a/src/Command/Project/ProjectSetRemoteCommand.php b/src/Command/Project/ProjectSetRemoteCommand.php index 405001cfea..ba1bf417c8 100644 --- a/src/Command/Project/ProjectSetRemoteCommand.php +++ b/src/Command/Project/ProjectSetRemoteCommand.php @@ -66,7 +66,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $this->git->ensureInstalled(); $gitRemotes = []; - foreach ([$this->config->get('detection.git_remote_name'), 'origin'] as $remote) { + foreach ([$this->config->getStr('detection.git_remote_name'), 'origin'] as $remote) { $url = $this->git->getConfig(sprintf('remote.%s.url', $remote)); if (\is_string($url) && $localProject->parseGitUrl($url) !== false) { $gitRemotes[$remote] = $url; diff --git a/src/Command/Team/TeamListCommand.php b/src/Command/Team/TeamListCommand.php index 5213fde5e6..21d6d5cd45 100644 --- a/src/Command/Team/TeamListCommand.php +++ b/src/Command/Team/TeamListCommand.php @@ -176,7 +176,7 @@ private function loadTeamsOnProject(Project $project): array } catch (BadResponseException $e) { throw ApiResponseException::create($e->getRequest(), $e->getResponse(), $e); } - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); foreach ($data['items'] as $item) { $info[$item['team_id']] = $item['granted_at']; } diff --git a/src/Command/Version/VersionListCommand.php b/src/Command/Version/VersionListCommand.php index 4672522a51..9ec341e440 100644 --- a/src/Command/Version/VersionListCommand.php +++ b/src/Command/Version/VersionListCommand.php @@ -38,7 +38,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $httpClient = $this->api->getHttpClient(); $response = $httpClient->get($environment->getLink('#versions')); - $data = Utils::jsonDecode((string) $response->getBody(), true); + $data = (array) Utils::jsonDecode((string) $response->getBody(), true); $header = ['id' => 'ID', 'commit' => 'Commit', 'locked' => 'Locked', 'routing_percentage' => 'Routing %']; diff --git a/src/Command/WebCommand.php b/src/Command/WebCommand.php index fa7ed54259..c1bc51cdd4 100644 --- a/src/Command/WebCommand.php +++ b/src/Command/WebCommand.php @@ -53,7 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($environmentId !== null) { // Console links lack the /environments path component. $isConsole = $this->config->has('detection.console_domain') - && parse_url((string) $url, PHP_URL_HOST) === $this->config->get('detection.console_domain'); + && parse_url((string) $url, PHP_URL_HOST) === $this->config->getStr('detection.console_domain'); if ($isConsole) { $url .= '/' . rawurlencode((string) $environmentId); } else { diff --git a/src/Local/LocalProject.php b/src/Local/LocalProject.php index 995131b5fa..9326b3d141 100644 --- a/src/Local/LocalProject.php +++ b/src/Local/LocalProject.php @@ -54,7 +54,7 @@ public function readProjectConfigFile(string $dir, string $configFile): ?array */ public function parseGitUrl(string $gitUrl): false|array { - $gitDomain = $this->config->get('detection.git_domain'); + $gitDomain = $this->config->getStr('detection.git_domain'); $pattern = '/^([a-z0-9]{12,})@git\.(([a-z0-9\-]+\.)?' . preg_quote((string) $gitDomain) . '):\1\.git$/'; if (!preg_match($pattern, $gitUrl, $matches)) { return false; @@ -77,7 +77,7 @@ public function parseGitUrl(string $gitUrl): false|array protected function getGitRemoteUrl(string $dir): string|false { $this->git->ensureInstalled(); - foreach ([$this->config->get('detection.git_remote_name'), 'origin'] as $remote) { + foreach ([$this->config->getStr('detection.git_remote_name'), 'origin'] as $remote) { if ($url = $this->git->getConfig("remote.$remote.url", $dir)) { return $url; } @@ -101,12 +101,12 @@ public function ensureGitRemote(string $dir, string $url): void } $this->git->ensureInstalled(); $currentUrl = $this->git->getConfig( - sprintf('remote.%s.url', $this->config->get('detection.git_remote_name')), + sprintf('remote.%s.url', $this->config->getStr('detection.git_remote_name')), $dir ); if (!$currentUrl) { $this->git->execute( - ['remote', 'add', $this->config->get('detection.git_remote_name'), $url], + ['remote', 'add', $this->config->getStr('detection.git_remote_name'), $url], $dir, true ); @@ -114,7 +114,7 @@ public function ensureGitRemote(string $dir, string $url): void $this->git->execute([ 'remote', 'set-url', - $this->config->get('detection.git_remote_name'), + $this->config->getStr('detection.git_remote_name'), $url ], $dir, true); } diff --git a/src/Service/Identifier.php b/src/Service/Identifier.php index e4f10dcfcf..ba2ee3b869 100644 --- a/src/Service/Identifier.php +++ b/src/Service/Identifier.php @@ -109,7 +109,7 @@ private function parseProjectId(string $url): array } if ($this->config->has('detection.console_domain') - && $host === $this->config->get('detection.console_domain') + && $host === $this->config->getStr('detection.console_domain') && preg_match('#^/[a-z0-9-]+/([a-z0-9-]+)(/([^/]+))?#', $path, $matches) // Console uses /-/ to distinguish sub-paths and identifiers. && $matches[1] !== '-') { @@ -175,7 +175,7 @@ private function getClusterHeader(string $url): string|false return false; } } - $cluster = $response->getHeader($this->config->get('detection.cluster_header')); + $cluster = $response->getHeader($this->config->getStr('detection.cluster_header')); $canCache = !empty($cluster) || ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300); if ($canCache) { diff --git a/src/Util/SslUtil.php b/src/Util/SslUtil.php index 72090891f6..4c27f42d26 100644 --- a/src/Util/SslUtil.php +++ b/src/Util/SslUtil.php @@ -16,9 +16,8 @@ class SslUtil * * @throws \InvalidArgumentException * - * @return array - * An array containing the contents of the certificate files, keyed as - * 'certificate' (string), 'key' (string), and 'chain' (array). + * @return array{certificate: string, key: string, chain: string[]} + * An array containing the contents of the certificate files. */ public function validate(string $certPath, string $keyPath, array $chainPaths): array {