diff --git a/php/public/index.php b/php/public/index.php index cc06bb90..fc99f4e4 100644 --- a/php/public/index.php +++ b/php/public/index.php @@ -20,7 +20,9 @@ use Psr\Http\Message\ServerRequestInterface as Request; require __DIR__ . '/../vendor/autoload.php'; +/** @var \DI\Container */ $container = \AIO\DependencyInjection::GetContainer(); +/** @var \AIO\Data\DataConst */ $dataConst = $container->get(\AIO\Data\DataConst::class); ini_set('session.save_path', $dataConst->GetSessionDirectory()); diff --git a/php/src/ContainerDefinitionFetcher.php b/php/src/ContainerDefinitionFetcher.php index d2519ed7..7be3d0cc 100644 --- a/php/src/ContainerDefinitionFetcher.php +++ b/php/src/ContainerDefinitionFetcher.php @@ -22,8 +22,10 @@ readonly class ContainerDefinitionFetcher { public function GetContainerById(string $id): Container { + /** @var array */ $containers = $this->FetchDefinition(); + /** @psalm-var \AIO\Container\Container $container */ foreach ($containers as $container) { if ($container->identifier === $id) { return $container; @@ -38,22 +40,26 @@ readonly class ContainerDefinitionFetcher { */ private function GetDefinition(): array { + /** @psalm-var array $data */ $data = json_decode((string)file_get_contents(DataConst::GetContainersDefinitionPath()), true, 512, JSON_THROW_ON_ERROR); $additionalContainerNames = []; + /** @psalm-var string $communityContainer */ foreach ($this->configurationManager->aioCommunityContainers as $communityContainer) { if ($communityContainer !== '') { $path = DataConst::GetCommunityContainersDirectory() . '/' . $communityContainer . '/' . $communityContainer . '.json'; + /** @psalm-var array $additionalData */ $additionalData = json_decode((string)file_get_contents($path), true, 512, JSON_THROW_ON_ERROR); $data = array_merge_recursive($data, $additionalData); if (isset($additionalData['aio_services_v1'][0]['display_name']) && $additionalData['aio_services_v1'][0]['display_name'] !== '') { // Store container_name of community containers in variable for later - $additionalContainerNames[] = $additionalData['aio_services_v1'][0]['container_name']; + $additionalContainerNames[] = (string) $additionalData['aio_services_v1'][0]['container_name']; } } } $containers = []; + /** @psalm-var array $entry */ foreach ($data['aio_services_v1'] as $entry) { if ($entry['container_name'] === 'nextcloud-aio-clamav') { if (!$this->configurationManager->isClamavEnabled) { @@ -98,6 +104,7 @@ readonly class ContainerDefinitionFetcher { $ports = new ContainerPorts(); if (isset($entry['ports'])) { + /** @psalm-var array $value */ foreach ($entry['ports'] as $value) { $ports->AddPort( new ContainerPort( @@ -111,6 +118,7 @@ readonly class ContainerDefinitionFetcher { $volumes = new ContainerVolumes(); if (isset($entry['volumes'])) { + /** @psalm-var array $value */ foreach ($entry['volumes'] as $value) { if($value['source'] === '%BORGBACKUP_HOST_LOCATION%') { $value['source'] = $this->configurationManager->borgBackupHostLocation; @@ -157,15 +165,18 @@ readonly class ContainerDefinitionFetcher { $dependsOn = []; if (isset($entry['depends_on'])) { + /** @var array */ $valueDependsOn = $entry['depends_on']; if ($entry['container_name'] === 'nextcloud-aio-apache') { // Add community containers first and default ones last so that aio_variables works correctly $valueDependsOnTemp = []; + /** @psalm-var string $containerName */ foreach ($additionalContainerNames as $containerName) { $valueDependsOnTemp[] = $containerName; } $valueDependsOn = array_merge_recursive($valueDependsOnTemp, $valueDependsOn); } + /** @psalm-var string $value */ foreach ($valueDependsOn as $value) { if ($value === 'nextcloud-aio-clamav') { if (!$this->configurationManager->isClamavEnabled) { @@ -210,6 +221,7 @@ readonly class ContainerDefinitionFetcher { $variables = new ContainerEnvironmentVariables(); if (isset($entry['environment'])) { + /** @psalm-var string $value */ foreach ($entry['environment'] as $value) { $variables->AddVariable($value); } @@ -217,6 +229,7 @@ readonly class ContainerDefinitionFetcher { $aioVariables = new AioVariables(); if (isset($entry['aio_variables'])) { + /** @psalm-var string $value */ foreach ($entry['aio_variables'] as $value) { $aioVariables->AddVariable($value); } @@ -224,27 +237,28 @@ readonly class ContainerDefinitionFetcher { $displayName = ''; if (isset($entry['display_name'])) { - $displayName = $entry['display_name']; + $displayName = (string) $entry['display_name']; } $restartPolicy = ''; if (isset($entry['restart'])) { - $restartPolicy = $entry['restart']; + $restartPolicy = (string) $entry['restart']; } $maxShutdownTime = 10; if (isset($entry['stop_grace_period'])) { - $maxShutdownTime = $entry['stop_grace_period']; + $maxShutdownTime = (int) $entry['stop_grace_period']; } $internalPort = ''; if (isset($entry['internal_port'])) { - $internalPort = $entry['internal_port']; + $internalPort = (string) $entry['internal_port']; } if (isset($entry['secrets'])) { // All secrets are registered with the configuration when they // are discovered so they can be later generated at time-of-use. + /** @psalm-var string $secret */ foreach ($entry['secrets'] as $secret) { $this->configurationManager->registerSecret($secret); } @@ -252,67 +266,72 @@ readonly class ContainerDefinitionFetcher { $uiSecret = ''; if (isset($entry['ui_secret'])) { - $uiSecret = $entry['ui_secret']; + $uiSecret = (string) $entry['ui_secret']; } $devices = []; if (isset($entry['devices'])) { + /** @var array */ $devices = $entry['devices']; } $enableNvidiaGpu = false; if (isset($entry['enable_nvidia_gpu'])) { - $enableNvidiaGpu = $entry['enable_nvidia_gpu']; + $enableNvidiaGpu = (bool) $entry['enable_nvidia_gpu']; } $capAdd = []; if (isset($entry['cap_add'])) { + /** @var array */ $capAdd = $entry['cap_add']; } $shmSize = -1; if (isset($entry['shm_size'])) { - $shmSize = $entry['shm_size']; + $shmSize = (int) $entry['shm_size']; } $apparmorUnconfined = false; if (isset($entry['apparmor_unconfined'])) { - $apparmorUnconfined = $entry['apparmor_unconfined']; + $apparmorUnconfined = (bool) $entry['apparmor_unconfined']; } $backupVolumes = []; if (isset($entry['backup_volumes'])) { + /** @var array */ $backupVolumes = $entry['backup_volumes']; } $nextcloudExecCommands = []; if (isset($entry['nextcloud_exec_commands'])) { + /** @var array */ $nextcloudExecCommands = $entry['nextcloud_exec_commands']; } $readOnlyRootFs = false; if (isset($entry['read_only'])) { - $readOnlyRootFs = $entry['read_only']; + $readOnlyRootFs = (bool) $entry['read_only']; } $tmpfs = []; if (isset($entry['tmpfs'])) { + /** @var array */ $tmpfs = $entry['tmpfs']; } $init = true; if (isset($entry['init'])) { - $init = $entry['init']; + $init = (bool) $entry['init']; } $imageTag = '%AIO_CHANNEL%'; if (isset($entry['image_tag'])) { - $imageTag = $entry['image_tag']; + $imageTag = (string) $entry['image_tag']; } $documentation = ''; if (isset($entry['documentation'])) { - $documentation = $entry['documentation']; + $documentation = (string) $entry['documentation']; } $containers[] = new Container( diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index c40ee98c..09be2abe 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -19,26 +19,34 @@ readonly class ConfigurationController { try { $this->configurationManager->startTransaction(); if (isset($request->getParsedBody()['domain'])) { + /** @var string */ $domain = $request->getParsedBody()['domain'] ?? ''; $skipDomainValidation = isset($request->getParsedBody()['skip_domain_validation']); $this->configurationManager->setDomain($domain, $skipDomainValidation); } if (isset($request->getParsedBody()['current-master-password']) || isset($request->getParsedBody()['new-master-password'])) { + /** @var string */ $currentMasterPassword = $request->getParsedBody()['current-master-password'] ?? ''; + /** @var string */ $newMasterPassword = $request->getParsedBody()['new-master-password'] ?? ''; $this->configurationManager->changeMasterPassword($currentMasterPassword, $newMasterPassword); } if (isset($request->getParsedBody()['borg_backup_host_location']) || isset($request->getParsedBody()['borg_remote_repo'])) { + /** @var string */ $location = $request->getParsedBody()['borg_backup_host_location'] ?? ''; + /** @var string */ $borgRemoteRepo = $request->getParsedBody()['borg_remote_repo'] ?? ''; $this->configurationManager->setBorgLocationVars($location, $borgRemoteRepo); } if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_remote_repo']) || isset($request->getParsedBody()['borg_restore_password'])) { + /** @var string */ $restoreLocation = $request->getParsedBody()['borg_restore_host_location'] ?? ''; + /** @var string */ $borgRemoteRepo = $request->getParsedBody()['borg_restore_remote_repo'] ?? ''; + /** @var string */ $borgPassword = $request->getParsedBody()['borg_restore_password'] ?? ''; $this->configurationManager->setBorgRestoreLocationVarsAndPassword($restoreLocation, $borgRemoteRepo, $borgPassword); } @@ -54,6 +62,7 @@ readonly class ConfigurationController { } else { $successNotification = false; } + /** @var string */ $dailyBackupTime = $request->getParsedBody()['daily_backup_time'] ?? ''; $this->configurationManager->setDailyBackupTime($dailyBackupTime, $enableAutomaticUpdates, $successNotification); } @@ -63,6 +72,7 @@ readonly class ConfigurationController { } if (isset($request->getParsedBody()['additional_backup_directories'])) { + /** @var string */ $additionalBackupDirectories = $request->getParsedBody()['additional_backup_directories'] ?? ''; $this->configurationManager->setAdditionalBackupDirectories($additionalBackupDirectories); } @@ -72,11 +82,13 @@ readonly class ConfigurationController { } if (isset($request->getParsedBody()['timezone'])) { + /** @var string */ $timezone = $request->getParsedBody()['timezone'] ?? ''; $this->configurationManager->timezone = $timezone; } if (isset($request->getParsedBody()['options-form'])) { + /** @var string */ $officeSuiteChoice = $request->getParsedBody()['office_suite_choice'] ?? ''; if ($officeSuiteChoice === 'collabora') { @@ -103,6 +115,7 @@ readonly class ConfigurationController { $enabledCC = []; /** * @psalm-suppress PossiblyNullIterator + * @psalm-var string $item */ foreach ($request->getParsedBody() as $item) { if (array_key_exists($item , $cc)) { @@ -117,6 +130,7 @@ readonly class ConfigurationController { } if (isset($request->getParsedBody()['collabora_dictionaries'])) { + /** @var string */ $collaboraDictionaries = $request->getParsedBody()['collabora_dictionaries'] ?? ''; $this->configurationManager->collaboraDictionaries = $collaboraDictionaries; } @@ -126,6 +140,7 @@ readonly class ConfigurationController { } if (isset($request->getParsedBody()['collabora_additional_options'])) { + /** @var string */ $additionalCollaboraOptions = $request->getParsedBody()['collabora_additional_options'] ?? ''; $this->configurationManager->collaboraAdditionalOptions = $additionalCollaboraOptions; } diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php index 81b920d0..9ab16464 100644 --- a/php/src/Controller/DockerController.php +++ b/php/src/Controller/DockerController.php @@ -125,6 +125,7 @@ readonly class DockerController { public function StartBackupContainerRestore(Request $request, Response $response, array $args) : Response { $this->configurationManager->startTransaction(); $this->configurationManager->backupMode = 'restore'; + /** @var string */ $this->configurationManager->selectedRestoreTime = $request->getParsedBody()['selected_restore_time'] ?? ''; $this->configurationManager->restoreExcludePreviews = isset($request->getParsedBody()['restore-exclude-previews']); $this->configurationManager->commitTransaction(); @@ -171,6 +172,7 @@ readonly class DockerController { $uri = $request->getUri(); $host = $uri->getHost(); $port = $uri->getPort(); + /** @var string */ $path = $request->getParsedBody()['base_path'] ?? ''; if ($port === 8000) { error_log('The AIO_URL-port was discovered to be 8000 which is not expected. It is now set to 443.'); @@ -284,6 +286,7 @@ readonly class DockerController { return; // Don't start if domaincheck is already running } elseif ($domaincheckContainer->GetRunningState() === ContainerState::Running) { + /** @psalm-var mixed $domaincheckWasStarted */ $domaincheckWasStarted = apcu_fetch($cacheKey); // Start domaincheck again when 10 minutes are over by not returning here if($domaincheckWasStarted !== false && is_string($domaincheckWasStarted)) { diff --git a/php/src/Controller/LoginController.php b/php/src/Controller/LoginController.php index 412ff9df..b4b3f6bd 100644 --- a/php/src/Controller/LoginController.php +++ b/php/src/Controller/LoginController.php @@ -21,6 +21,7 @@ readonly class LoginController { $response->getBody()->write("The login is blocked since Nextcloud is running."); return $response->withHeader('Location', '.')->withStatus(422); } + /** @var string */ $password = $request->getParsedBody()['password'] ?? ''; if($this->authManager->CheckCredentials($password)) { $this->authManager->SetAuthState(true); @@ -32,6 +33,7 @@ readonly class LoginController { } public function GetTryLogin(Request $request, Response $response, array $args) : Response { + /** @var string */ $token = $request->getQueryParams()['token'] ?? ''; if($this->authManager->CheckToken($token)) { $this->authManager->SetAuthState(true); diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index 7534acda..8eb4e3ce 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -288,6 +288,7 @@ class ConfigurationManager if ($this->config === [] && file_exists(DataConst::GetConfigFile())) { $configContent = (string)file_get_contents(DataConst::GetConfigFile()); + /** @var array */ $this->config = json_decode($configContent, true, 512, JSON_THROW_ON_ERROR); } @@ -332,6 +333,7 @@ class ConfigurationManager return ''; } + /** @var array */ $secrets = $this->get('secrets', []); if (!isset($secrets[$secretId])) { $secrets[$secretId] = bin2hex(random_bytes(24)); @@ -466,15 +468,17 @@ class ConfigurationManager if ($this->shouldDomainValidationBeSkipped($skipDomainValidation)) { error_log('Skipping domain validation'); } else { + /** @var string */ $dnsRecordIP = gethostbyname($domain); if ($dnsRecordIP === $domain) { $dnsRecordIP = ''; } if (empty($dnsRecordIP)) { + /** @var array */ $record = dns_get_record($domain, DNS_AAAA); if (isset($record[0]['ipv6']) && !empty($record[0]['ipv6'])) { - $dnsRecordIP = $record[0]['ipv6']; + $dnsRecordIP = (string) $record[0]['ipv6']; } } @@ -694,6 +698,7 @@ class ConfigurationManager private function getEnvironmentalVariableOrConfig(string $envVariableName, string $configName, string $defaultValue) : string { $envVariableOutput = getenv($envVariableName); + /** @var mixed */ $configValue = $this->get($configName, ''); if ($envVariableOutput === false) { if ($configValue === '') { @@ -919,6 +924,7 @@ class ConfigurationManager $dir = array_diff($dir, array('..', '.', 'readme.md')); foreach ($dir as $id) { $filePath = DataConst::GetCommunityContainersDirectory() . '/' . $id . '/' . $id . '.json'; + /** @psalm-var mixed $fileContents */ $fileContents = apcu_fetch($filePath); if (!is_string($fileContents)) { $fileContents = file_get_contents($filePath); @@ -926,8 +932,11 @@ class ConfigurationManager apcu_add($filePath, $fileContents); } } + + /** @psalm-var array $json */ $json = is_string($fileContents) ? json_decode($fileContents, true, 512, JSON_THROW_ON_ERROR) : false; - if(is_array($json) && is_array($json['aio_services_v1'])) { + if(isset($json['aio_services_v1']) && is_array($json['aio_services_v1'])) { + /** @psalm-var array $service */ foreach ($json['aio_services_v1'] as $service) { $documentation = is_string($service['documentation']) ? $service['documentation'] : ''; if (is_string($service['display_name'])) { @@ -960,8 +969,9 @@ class ConfigurationManager return; } $this->startTransaction(); + /** @psalm-var string $variable */ foreach ($input as $variable) { - if (!is_string($variable) || !str_contains($variable, '=')) { + if (!str_contains($variable, '=')) { error_log("Invalid input: '$variable' is not a string or does not contain an equal sign ('=')"); continue; } diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php index 86b36619..1af835d8 100644 --- a/php/src/Docker/DockerActionManager.php +++ b/php/src/Docker/DockerActionManager.php @@ -54,6 +54,7 @@ readonly class DockerActionManager { throw $e; } + /** @var array */ $responseBody = json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR); if ($responseBody['State']['Running'] === true) { @@ -74,6 +75,7 @@ readonly class DockerActionManager { throw $e; } + /** @var array */ $responseBody = json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR); if ($responseBody['State']['Restarting'] === true) { @@ -98,6 +100,7 @@ readonly class DockerActionManager { return VersionState::Equal; } + /** @psalm-var string $runningDigest */ foreach ($runningDigests as $runningDigest) { if ($runningDigest === $remoteDigest) { return VersionState::Equal; @@ -334,6 +337,7 @@ readonly class DockerActionManager { } $tmpfs = []; + /** @psalm-var string $tmp */ foreach ($container->tmpfs as $tmp) { $mode = ""; if (str_contains($tmp, ':')) { @@ -374,6 +378,7 @@ readonly class DockerActionManager { // Special things for the backup container which should not be exposed in the containers.json if (str_starts_with($container->identifier, 'nextcloud-aio-borgbackup')) { // Additional backup directories + /** @psalm-var string $additionalBackupVolumes */ foreach ($this->getAllBackupVolumes() as $additionalBackupVolumes) { if ($additionalBackupVolumes !== '') { $mounts[] = ["Type" => "volume", "Source" => $additionalBackupVolumes, "Target" => "/nextcloud_aio_volumes/" . $additionalBackupVolumes, "ReadOnly" => false]; @@ -383,6 +388,7 @@ readonly class DockerActionManager { // Make volumes read only in case of borgbackup container. The viewer makes them writeable $isReadOnly = $container->identifier === 'nextcloud-aio-borgbackup'; + /** @psalm-var string $additionalBackupDirectories */ foreach ($this->configurationManager->getAdditionalBackupDirectoriesArray() as $additionalBackupDirectories) { if ($additionalBackupDirectories !== '') { if (!str_starts_with($additionalBackupDirectories, '/')) { @@ -578,6 +584,7 @@ readonly class DockerActionManager { $container = $this->containerDefinitionFetcher->GetContainerById($id); $nextcloudExecCommands = ''; + /** @psalm-var string $execCommand */ foreach ($container->nextcloudExecCommands as $execCommand) { $nextcloudExecCommands .= $execCommand . PHP_EOL; } @@ -595,10 +602,12 @@ readonly class DockerActionManager { private function GetRepoDigestsOfContainer(string $containerName): ?array { try { $containerUrl = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName)); + /** @var array */ $containerOutput = json_decode($this->guzzleClient->get($containerUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); - $imageName = $containerOutput['Image']; + $imageName = (string) $containerOutput['Image']; $imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName)); + /** @var array */ $imageOutput = json_decode($this->guzzleClient->get($imageUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); if (!isset($imageOutput['RepoDigests'])) { @@ -613,6 +622,7 @@ readonly class DockerActionManager { $repoDigestArray = []; $oneDigestGiven = false; + /** @psalm-var string $repoDigest */ foreach ($imageOutput['RepoDigests'] as $repoDigest) { $digestPosition = strpos($repoDigest, '@'); if ($digestPosition === false) { @@ -635,6 +645,7 @@ readonly class DockerActionManager { private function GetCurrentImageName(): string { $cacheKey = 'aio-image-name'; + /** @psalm-var mixed $imageName */ $imageName = apcu_fetch($cacheKey); if ($imageName !== false && is_string($imageName)) { return $imageName; @@ -643,13 +654,14 @@ readonly class DockerActionManager { $containerName = 'nextcloud-aio-mastercontainer'; $url = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName)); try { + /** @var array */ $output = json_decode($this->guzzleClient->get($url)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $imageNameArray = explode(':', $output['Config']['Image']); if (count($imageNameArray) === 2) { $imageName = $imageNameArray[0]; } else { error_log("No tag was found when getting the current channel. You probably did not follow the documentation correctly. Changing the imageName to the default " . $output['Config']['Image']); - $imageName = $output['Config']['Image']; + $imageName = (string) $output['Config']['Image']; } apcu_add($cacheKey, $imageName); return $imageName; @@ -662,6 +674,7 @@ readonly class DockerActionManager { public function GetCurrentChannel(): string { $cacheKey = 'aio-ChannelName'; + /** @psalm-var mixed $channelName */ $channelName = apcu_fetch($cacheKey); if ($channelName !== false && is_string($channelName)) { return $channelName; @@ -670,6 +683,7 @@ readonly class DockerActionManager { $containerName = 'nextcloud-aio-mastercontainer'; $url = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName)); try { + /** @var array */ $output = json_decode($this->guzzleClient->get($url)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $tagArray = explode(':', $output['Config']['Image']); if (count($tagArray) === 2) { @@ -702,6 +716,7 @@ readonly class DockerActionManager { return false; } + /** @psalm-var string $runningDigest */ foreach ($runningDigests as $runningDigest) { if ($remoteDigest === $runningDigest) { return false; @@ -717,6 +732,7 @@ readonly class DockerActionManager { // schedule the exec $url = $this->BuildApiUrl(sprintf('containers/%s/exec', urlencode($containerName))); + /** @var array */ $response = json_decode( $this->guzzleClient->request( 'POST', @@ -739,7 +755,7 @@ readonly class DockerActionManager { JSON_THROW_ON_ERROR, ); - $id = $response['Id']; + $id = (string) $response['Id']; // start the exec $url = $this->BuildApiUrl(sprintf('exec/%s/start', $id)); @@ -878,8 +894,10 @@ readonly class DockerActionManager { throw $e; } + /** @var array */ $responseBody = json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR); + /** @var null|int */ $exitCode = $responseBody['State']['ExitCode']; if (is_int($exitCode)) { return $exitCode; @@ -900,8 +918,10 @@ readonly class DockerActionManager { throw $e; } + /** @var array */ $responseBody = json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR); + /** @var null|int */ $exitCode = $responseBody['State']['ExitCode']; if (is_int($exitCode)) { return $exitCode; @@ -932,6 +952,7 @@ readonly class DockerActionManager { $imageName = $imageName . ':' . $this->GetCurrentChannel(); try { $imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName)); + /** @var array */ $imageOutput = json_decode($this->guzzleClient->get($imageUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); if (!isset($imageOutput['Created'])) { diff --git a/php/src/Docker/DockerHubManager.php b/php/src/Docker/DockerHubManager.php index 256d592e..767ce70c 100644 --- a/php/src/Docker/DockerHubManager.php +++ b/php/src/Docker/DockerHubManager.php @@ -17,6 +17,7 @@ readonly class DockerHubManager { public function GetLatestDigestOfTag(string $name, string $tag) : ?string { $cacheKey = 'dockerhub-manifest-' . $name . $tag; + /** @psalm-var mixed $cachedVersion */ $cachedVersion = apcu_fetch($cacheKey); if($cachedVersion !== false && is_string($cachedVersion)) { return $cachedVersion; @@ -30,9 +31,10 @@ readonly class DockerHubManager { 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:' . $name . ':pull' ); $body = $authTokenRequest->getBody()->getContents(); + /** @var array */ $decodedBody = json_decode($body, true, 512, JSON_THROW_ON_ERROR); if(isset($decodedBody['token'])) { - $authToken = $decodedBody['token']; + $authToken = (string) $decodedBody['token']; $manifestRequest = $this->guzzleClient->request( 'HEAD', 'https://registry-1.docker.io/v2/'.$name.'/manifests/' . $tag, diff --git a/php/src/Docker/GitHubContainerRegistryManager.php b/php/src/Docker/GitHubContainerRegistryManager.php index eeecfb28..08dee0d5 100644 --- a/php/src/Docker/GitHubContainerRegistryManager.php +++ b/php/src/Docker/GitHubContainerRegistryManager.php @@ -18,6 +18,7 @@ readonly class GitHubContainerRegistryManager { $cacheKey = 'ghcr-manifest-' . $name . $tag; + /** @psalm-var mixed $cachedVersion */ $cachedVersion = apcu_fetch($cacheKey); if ($cachedVersion !== false && is_string($cachedVersion)) { return $cachedVersion; @@ -31,9 +32,10 @@ readonly class GitHubContainerRegistryManager 'https://ghcr.io/token?scope=repository:' . $name . ':pull' ); $body = $authTokenRequest->getBody()->getContents(); + /** @var array */ $decodedBody = json_decode($body, true, 512, JSON_THROW_ON_ERROR); if (isset($decodedBody['token'])) { - $authToken = $decodedBody['token']; + $authToken = (string) $decodedBody['token']; $manifestRequest = $this->guzzleClient->request( 'HEAD', 'https://ghcr.io/v2/' . $name . '/manifests/' . $tag,