From f05ca4783c96394d20b0034a61299d883e3067a9 Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 20:37:31 +0100 Subject: [PATCH 01/11] Add further information to widgets --- Classes/Client/ExtensionSettingException.php | 14 ++ Classes/Client/PleskClientFactory.php | 62 ++++++ Classes/Configuration/ExtConf.php | 43 +++-- Classes/DataProvider/PleskDataProvider.php | 177 ------------------ Classes/DataProvider/ServerDataProvider.php | 52 +++++ Classes/DataProvider/WebspaceDataProvider.php | 118 ++++++++++++ Classes/Plesk/Hosting.php | 45 +++++ Classes/Plesk/Site.php | 50 +++-- Classes/Plesk/Site/GeneralInfo.php | 60 ------ Classes/Service/PleskSiteService.php | 54 ++++++ Classes/Widget/PhpWidget.php | 74 ++++++++ Classes/Widget/ServerWidget.php | 98 ++++++++++ Classes/Widget/WebspaceWidget.php | 96 ++++++++++ Classes/Widgets/Webspace.php | 134 ------------- Configuration/Services.yaml | 35 +++- Configuration/page.tsconfig | 1 + .../Templates/Dashboard/Widget/Webspace.html | 46 ----- Resources/Private/Templates/Widget/Php.html | 39 ++++ .../Private/Templates/Widget/Server.html | 32 ++++ .../Private/Templates/Widget/Webspace.html | 22 +++ composer.json | 5 +- ext_conf_template.txt | 14 +- ext_emconf.php | 6 +- 23 files changed, 811 insertions(+), 466 deletions(-) create mode 100644 Classes/Client/ExtensionSettingException.php create mode 100644 Classes/Client/PleskClientFactory.php delete mode 100644 Classes/DataProvider/PleskDataProvider.php create mode 100644 Classes/DataProvider/ServerDataProvider.php create mode 100644 Classes/DataProvider/WebspaceDataProvider.php create mode 100644 Classes/Plesk/Hosting.php delete mode 100644 Classes/Plesk/Site/GeneralInfo.php create mode 100644 Classes/Service/PleskSiteService.php create mode 100644 Classes/Widget/PhpWidget.php create mode 100644 Classes/Widget/ServerWidget.php create mode 100644 Classes/Widget/WebspaceWidget.php delete mode 100644 Classes/Widgets/Webspace.php create mode 100644 Configuration/page.tsconfig delete mode 100644 Resources/Private/Templates/Dashboard/Widget/Webspace.html create mode 100644 Resources/Private/Templates/Widget/Php.html create mode 100644 Resources/Private/Templates/Widget/Server.html create mode 100644 Resources/Private/Templates/Widget/Webspace.html diff --git a/Classes/Client/ExtensionSettingException.php b/Classes/Client/ExtensionSettingException.php new file mode 100644 index 0000000..c0599fd --- /dev/null +++ b/Classes/Client/ExtensionSettingException.php @@ -0,0 +1,14 @@ +validateExtConf() === false) { + throw new ExtensionSettingException( + 'Incomplete plesk widget extension settings. See logs for more details', + 1736610959 + ); + } + + $pleskClient = new Client($this->extConf->getHost(), $this->extConf->getPort()); + $pleskClient->setCredentials($this->extConf->getUsername(), $this->extConf->getPassword()); + + return $pleskClient; + } + + private function validateExtConf(): bool + { + if ($this->extConf->getHost() === '') { + $this->logger->error('Plesk host in extension settings can not be empty'); + return false; + } + + if ($this->extConf->getUsername() === '') { + $this->logger->error('Plesk user in extension settings can not be empty'); + return false; + } + + if ($this->extConf->getPassword() === '') { + $this->logger->error('Plesk password in extension settings can not be empty'); + return false; + } + + return true; + } +} diff --git a/Classes/Configuration/ExtConf.php b/Classes/Configuration/ExtConf.php index b8ed9fe..1c479a9 100644 --- a/Classes/Configuration/ExtConf.php +++ b/Classes/Configuration/ExtConf.php @@ -15,34 +15,36 @@ use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\SingletonInterface; -use TYPO3\CMS\Core\Utility\GeneralUtility; /* * This class streamlines all settings from extension settings */ class ExtConf implements SingletonInterface { - protected string $diskUsageType = '%'; + private string $host; - protected string $host = ''; + private int $port; - protected int $port = 8443; + private string $username; - protected string $username = ''; + private string $password; - protected string $password = ''; + private string $diskUsageType; - public function __construct() + private string $domain; + + public function __construct(ExtensionConfiguration $extensionConfiguration) { try { - $extConf = (array)GeneralUtility::makeInstance(ExtensionConfiguration::class) - ->get('plesk_widget'); + $extConf = (array)$extensionConfiguration->get('plesk_widget'); - $this->diskUsageType = trim((string)($extConf['diskUsageType'] ?? '')); $this->host = trim((string)($extConf['host'] ?? '')); $this->port = (int)($extConf['port'] ?? 8443); $this->username = trim((string)($extConf['username'] ?? '')); $this->password = trim((string)($extConf['password'] ?? '')); + + $this->diskUsageType = trim((string)($extConf['diskUsageType'] ?? '%')); + $this->domain = trim((string)($extConf['domain'] ?? '')); } catch (ExtensionConfigurationExtensionNotConfiguredException $extensionConfigurationExtensionNotConfiguredException) { // Do nothing. The values will still be empty. We catch that as Exception just before the first API call } catch (ExtensionConfigurationPathDoesNotExistException $extensionConfigurationPathDoesNotExistException) { @@ -50,11 +52,6 @@ public function __construct() } } - public function getDiskUsageType(): string - { - return $this->diskUsageType; - } - public function getHost(): string { return $this->host; @@ -67,13 +64,21 @@ public function getPort(): int public function getUsername(): string { - //return $this->username; - return ''; + return $this->username; } public function getPassword(): string { - //return $this->password; - return ''; + return $this->password; + } + + public function getDiskUsageType(): string + { + return $this->diskUsageType; + } + + public function getDomain(): string + { + return $this->domain; } } diff --git a/Classes/DataProvider/PleskDataProvider.php b/Classes/DataProvider/PleskDataProvider.php deleted file mode 100644 index c1162b2..0000000 --- a/Classes/DataProvider/PleskDataProvider.php +++ /dev/null @@ -1,177 +0,0 @@ -extConf = $extConf; - $this->pleskClient = new Client($this->extConf->getHost(), $this->extConf->getPort()); - $this->pleskClient->setCredentials($this->extConf->getUsername(), $this->extConf->getPassword()); - } - - public function getChartData(): array - { - return [ - 'labels' => [ - 0 => 'HTTP', - 1 => 'Database', - 2 => 'Logs', - 3 => 'Free' - ], - 'datasets' => [ - [ - 'backgroundColor' => WidgetApi::getDefaultChartColors(), - 'border' => 0, - 'data' => $this->getWebSpaceStatus() - ] - ] - ]; - } - - public function getHosting(): array - { - $hostingInfo = $this->pleskClient->site()->getHosting(null, null); - if ($hostingInfo instanceof HostingInfo) { - return $hostingInfo->properties; - } - - return []; - } - - public function getCustomer(): GeneralInfo - { - $customers = $this->pleskClient->customer()->getAll(); - - return current($customers); - } - - protected function getWebSpaceStatus(): array - { - $diskUsage = $this->getDiskUsage(); - $diskSpace = (int)$this->getLimit('disk_space')->value; - - return [ - 0 => $this->calcDiskUsage( - ($diskUsage->httpdocs + $diskUsage->httpsdocs), - $diskSpace - ), - 1 => $this->calcDiskUsage( - $diskUsage->dbases, - $diskSpace - ), - 2 => $this->calcDiskUsage( - $diskUsage->logs, - $diskSpace - ), - 3 => $this->calcDiskUsage( - ($diskSpace - $diskUsage->httpdocs + $diskUsage->httpsdocs + $diskUsage->dbases + $diskUsage->logs), - $diskSpace - ) - ]; - } - - protected function calcDiskUsage(int $part, int $total = 0): float - { - if ($this->extConf->getDiskUsageType() === '%' && $total !== 0) { - $value = round(100 / $total * $part, 4); - } elseif ($this->extConf->getDiskUsageType() === 'MB') { - $value = round($part / 1024 / 1024, 4); - } elseif ($this->extConf->getDiskUsageType() === 'GB') { - $value = round($part / 1024 / 1024 / 1024, 4); - } else { - $value = (float)$part; - } - - return $value; - } - - protected function getLimit(string $limit): \StefanFroemken\PleskWidget\Plesk\Webspace\Limit - { - return $this->getLimits()->limits[$limit]; - } - - public function getLoginLink(): string - { - // Get external IP address. Works also within DDEV/Docker containers - $externalIpAddress = file_get_contents('http://ipecho.net/plain'); - - if (GeneralUtility::validIP($externalIpAddress)) { - return sprintf( - '%s://%s:%d/enterprise/rsession_init.php?PLESKSESSID=%s&success_redirect_url=%s', - $this->pleskClient->getProtocol() ?: 'https', - $this->pleskClient->getHost(), - $this->pleskClient->getPort(), - $this->pleskClient->server()->createSession($this->extConf->getUsername(), $externalIpAddress), - '/smb/web/view' - ); - } - - return sprintf( - '%s://%s:%d', - $this->pleskClient->getProtocol() ?: 'https', - $this->pleskClient->getHost(), - $this->pleskClient->getPort() - ); - } - - public function getLimits(): \StefanFroemken\PleskWidget\Plesk\Webspace\Limits - { - $packet = $this->pleskClient->getPacket(); - $getTag = $packet->addChild('webspace')->addChild('get'); - $getTag->addChild('filter'); - $getTag->addChild('dataset')->addChild('limits'); - $response = $this->pleskClient->request($packet, \PleskX\Api\Client::RESPONSE_FULL); - - $items = []; - foreach ($response->xpath('//result') as $xmlResult) { - $items[] = new Limits($xmlResult->data->limits); - } - return $items[0]; - } - - public function getSite(): \StefanFroemken\PleskWidget\Plesk\Site - { - $packet = $this->pleskClient->getPacket(); - $getTag = $packet->addChild('site')->addChild('get'); - $getTag->addChild('filter'); - $getTag->addChild('dataset')->addChild('gen_info'); - $response = $this->pleskClient->request($packet, \PleskX\Api\Client::RESPONSE_FULL); - - $sites = []; - foreach ($response->xpath('//result') as $xmlResult) { - $site = new \StefanFroemken\PleskWidget\Plesk\Site($xmlResult); - $site->genInfo = new \StefanFroemken\PleskWidget\Plesk\Site\GeneralInfo($xmlResult->data->gen_info); - $sites[] = $site; - } - return $sites[0]; - } - - public function getDiskUsage(): \PleskX\Api\Struct\Webspace\DiskUsage - { - return $this->pleskClient->webspace()->getDiskUsage(null, null); - } -} diff --git a/Classes/DataProvider/ServerDataProvider.php b/Classes/DataProvider/ServerDataProvider.php new file mode 100644 index 0000000..e1b908c --- /dev/null +++ b/Classes/DataProvider/ServerDataProvider.php @@ -0,0 +1,52 @@ +customer()->getAll(); + + return current($customers); + } + + public function getLoginLink(Client $pleskClient, string $externalIpAddress): string + { + if (GeneralUtility::validIP($externalIpAddress)) { + // Return direct login link + return sprintf( + '%s://%s:%d/enterprise/rsession_init.php?PLESKSESSID=%s&success_redirect_url=%s', + $pleskClient->getProtocol() ?: 'https', + $pleskClient->getHost(), + $pleskClient->getPort(), + $pleskClient->server()->createSession($this->extConf->getUsername(), $externalIpAddress), + '/smb/web/view' + ); + } + + // Return link to login form + return sprintf( + '%s://%s:%d', + $pleskClient->getProtocol() ?: 'https', + $pleskClient->getHost(), + $pleskClient->getPort() + ); + } +} diff --git a/Classes/DataProvider/WebspaceDataProvider.php b/Classes/DataProvider/WebspaceDataProvider.php new file mode 100644 index 0000000..bf8fc6a --- /dev/null +++ b/Classes/DataProvider/WebspaceDataProvider.php @@ -0,0 +1,118 @@ +pleskClientFactory->create(); + } catch (ExtensionSettingException $e) { + return []; + } + + return [ + 'labels' => [ + 0 => 'HTTP', + 1 => 'Database', + 2 => 'Logs', + 3 => 'Free' + ], + 'datasets' => [ + [ + 'backgroundColor' => WidgetApi::getDefaultChartColors(), + 'border' => 0, + 'data' => $this->getWebSpaceStatus($pleskClient) + ] + ] + ]; + } + + private function getWebSpaceStatus(Client $pleskClient): array + { + $diskUsage = $this->getDiskUsage($pleskClient); + $diskSpace = (int)$this->getLimit('disk_space', $pleskClient)->value; + + return [ + 0 => $this->calcDiskUsage( + ($diskUsage->httpdocs + $diskUsage->httpsdocs), + $diskSpace + ), + 1 => $this->calcDiskUsage( + $diskUsage->dbases, + $diskSpace + ), + 2 => $this->calcDiskUsage( + $diskUsage->logs, + $diskSpace + ), + 3 => $this->calcDiskUsage( + ($diskSpace - $diskUsage->httpdocs + $diskUsage->httpsdocs + $diskUsage->dbases + $diskUsage->logs), + $diskSpace + ) + ]; + } + + private function calcDiskUsage(int $part, int $total = 0): float + { + if ($this->extConf->getDiskUsageType() === '%' && $total !== 0) { + $value = round(100 / $total * $part, 4); + } elseif ($this->extConf->getDiskUsageType() === 'MB') { + $value = round($part / 1024 / 1024, 4); + } elseif ($this->extConf->getDiskUsageType() === 'GB') { + $value = round($part / 1024 / 1024 / 1024, 4); + } else { + $value = (float)$part; + } + + return $value; + } + + private function getLimit(string $limit, Client $pleskClient): \StefanFroemken\PleskWidget\Plesk\Webspace\Limit + { + return $this->getLimits($pleskClient)->limits[$limit]; + } + + private function getLimits(Client $pleskClient): \StefanFroemken\PleskWidget\Plesk\Webspace\Limits + { + $packet = $pleskClient->getPacket(); + $getTag = $packet->addChild('webspace')->addChild('get'); + $getTag->addChild('filter'); + $getTag->addChild('dataset')->addChild('limits'); + $response = $pleskClient->request($packet, Client::RESPONSE_FULL); + + $items = []; + foreach ($response->xpath('//result') as $xmlResult) { + $items[] = new Limits($xmlResult->data->limits); + } + return $items[0]; + } + + private function getDiskUsage(Client $pleskClient): \PleskX\Api\Struct\Webspace\DiskUsage + { + return $pleskClient->webspace()->getDiskUsage(null, null); + } +} diff --git a/Classes/Plesk/Hosting.php b/Classes/Plesk/Hosting.php new file mode 100644 index 0000000..d802a07 --- /dev/null +++ b/Classes/Plesk/Hosting.php @@ -0,0 +1,45 @@ +ipAddresses[] = (string)$xmlIpAddress; + } + foreach ($xmlProperties as $xmlProperty) { + $this->properties[(string)$xmlProperty->name] = (string)$xmlProperty->value; + } + } + + public function getIpAddresses(): array + { + return $this->ipAddresses; + } + + public function getProperties(): array + { + return $this->properties; + } +} diff --git a/Classes/Plesk/Site.php b/Classes/Plesk/Site.php index c30a424..f889615 100644 --- a/Classes/Plesk/Site.php +++ b/Classes/Plesk/Site.php @@ -11,26 +11,48 @@ namespace StefanFroemken\PleskWidget\Plesk; -class Site extends \PleskX\Api\Struct +use PleskX\Api\Client; +use PleskX\Api\Struct\Site\GeneralInfo; + +class Site { - /** @var string */ - public $filterId; + private Client $pleskClient; - /** @var string */ - public $id; + private GeneralInfo $generalInformation; - /** @var string */ - public $status; + public function __construct(Client $pleskClient, GeneralInfo $generalInfo) + { + $this->pleskClient = $pleskClient; + $this->generalInformation = $generalInfo; + } - /** @var \StefanFroemken\PleskWidget\Plesk\Site\GeneralInfo */ - public $genInfo; + public function getGeneralInformation(): GeneralInfo + { + return $this->generalInformation; + } - public function __construct($apiResponse) + public function getHosting(): ?Hosting { - $this->_initScalarProperties($apiResponse, [ - 'filter-id', - 'id', - 'status', + $response = $this->pleskClient->request([ + 'site' => [ + 'get' => [ + 'filter' => [ + 'guid' => $this->generalInformation->guid, + ], + 'dataset' => [ + 'hosting' => null, + ] + ], + ], ]); + + if (!property_exists($response->data->hosting, 'vrt_hst')) { + return null; + } + + return new Hosting( + $response->data->hosting->vrt_hst->ip_address, + $response->data->hosting->vrt_hst->property + ); } } diff --git a/Classes/Plesk/Site/GeneralInfo.php b/Classes/Plesk/Site/GeneralInfo.php deleted file mode 100644 index fd7d271..0000000 --- a/Classes/Plesk/Site/GeneralInfo.php +++ /dev/null @@ -1,60 +0,0 @@ -_initScalarProperties($apiResponse, [ - 'ascii-name', - 'cr_date', - 'description', - 'guid', - 'htype', - 'name', - 'real_size', - 'status', - ]); - - foreach ($apiResponse->dns_ip_address as $address) { - $this->dnsIpAddress[] = $address; - } - } -} diff --git a/Classes/Service/PleskSiteService.php b/Classes/Service/PleskSiteService.php new file mode 100644 index 0000000..dc2f7f6 --- /dev/null +++ b/Classes/Service/PleskSiteService.php @@ -0,0 +1,54 @@ +cache->has('plesk-widget-sites')) { + return $this->cache->get('plesk-widget-sites'); + } + + $sites = []; + foreach ($pleskClient->site()->getAll() as $generalInfo) { + $sites[] = new Site($pleskClient, $generalInfo); + } + + $this->cache->set('plesk-widget-sites', $sites); + + return $sites; + } + + public function getSiteByName(string $name, Client $pleskClient): ?Site + { + foreach ($this->getSites($pleskClient) as $site) { + if ($site->getGeneralInformation()->name === $name) { + return $site; + } + } + + return null; + } +} diff --git a/Classes/Widget/PhpWidget.php b/Classes/Widget/PhpWidget.php new file mode 100644 index 0000000..42a98f8 --- /dev/null +++ b/Classes/Widget/PhpWidget.php @@ -0,0 +1,74 @@ +request = $request; + } + + public function renderWidgetContent(): string + { + $view = $this->backendViewFactory->create($this->request); + $variables = [ + 'configuration' => $this->configuration, + ]; + + try { + $pleskClient = $this->pleskClientFactory->create(); + + if ($this->extConf->getDomain() === '') { + $variables['error'] = 'You have to select a domain name in extension settings of EXT:plesk-widget.'; + } elseif ($site = $this->pleskSiteService->getSiteByName($this->extConf->getDomain(), $pleskClient)) { + $variables['domain'] = $this->extConf->getDomain(); + $variables['hosting'] = $site->getHosting(); + } else { + $variables['error'] = 'Plesk API can not retrieve domain information for ' . $this->extConf->getDomain(); + } + } catch (ExtensionSettingException $extensionSettingException) { + $variables['error'] = $extensionSettingException->getMessage(); + } + + $view->assignMultiple($variables); + + return $view->render('Widget/Php'); + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Widget/ServerWidget.php b/Classes/Widget/ServerWidget.php new file mode 100644 index 0000000..66337a8 --- /dev/null +++ b/Classes/Widget/ServerWidget.php @@ -0,0 +1,98 @@ +request = $request; + } + + public function renderWidgetContent(): string + { + $view = $this->backendViewFactory->create($this->request); + $variables = [ + 'configuration' => $this->configuration, + ]; + + try { + $pleskClient = $this->pleskClientFactory->create(); + $externalIpAddress = $this->getExternalIpAddress(); + + $variables['customer'] = $this->dataProvider->getCustomer($pleskClient); + $variables['externalIpAddress'] = $externalIpAddress; + $variables['button'] = $this->getButtonProvider($pleskClient, $externalIpAddress); + + if ( + $this->extConf->getDomain() + && ($site = $this->pleskSiteService->getSiteByName($this->extConf->getDomain(), $pleskClient)) + && $ipAddresses = $site->getHosting()->getIpAddresses() + ) { + $variables['ipAddresses'] = $ipAddresses; + } + } catch (ExtensionSettingException $extensionSettingException) { + $variables['error'] = $extensionSettingException->getMessage(); + } + + $view->assignMultiple($variables); + + return $view->render('Widget/Server'); + } + + protected function getButtonProvider(Client $pleskClient, string $externalIpAddress): ButtonProvider + { + return GeneralUtility::makeInstance( + ButtonProvider::class, + 'Login to Plesk', + $this->dataProvider->getLoginLink($pleskClient, $externalIpAddress), + '_blank' + ); + } + + private function getExternalIpAddress(): string + { + // Get external IP address. Works also within DDEV/Docker containers + return file_get_contents('http://ipecho.net/plain'); + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Widget/WebspaceWidget.php b/Classes/Widget/WebspaceWidget.php new file mode 100644 index 0000000..05f40b7 --- /dev/null +++ b/Classes/Widget/WebspaceWidget.php @@ -0,0 +1,96 @@ +request = $request; + } + + public function renderWidgetContent(): string + { + $view = $this->backendViewFactory->create($this->request); + + $view->assign('configuration', $this->configuration); + + return $view->render('Widget/Webspace'); + } + + public function getEventData(): array + { + return [ + 'graphConfig' => [ + 'type' => 'doughnut', + 'options' => [ + 'maintainAspectRatio' => false, + 'plugins' => [ + 'legend' => [ + 'display' => true, + 'position' => 'bottom', + ], + 'title' => [ + 'display' => true, + 'text' => 'Usage in ' . $this->extConf->getDiskUsageType() + ], + 'tooltip' => [ + 'enabled' => true, + ], + ], + 'cutoutPercentage' => 60, + ], + 'data' => $this->dataProvider->getChartData(), + ], + ]; + } + + public function getCssFiles(): array + { + return []; + } + + public function getJavaScriptModuleInstructions(): array + { + return [ + JavaScriptModuleInstruction::create('@typo3/dashboard/contrib/chartjs.js'), + JavaScriptModuleInstruction::create('@typo3/dashboard/chart-initializer.js'), + ]; + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Widgets/Webspace.php b/Classes/Widgets/Webspace.php deleted file mode 100644 index cd9a513..0000000 --- a/Classes/Widgets/Webspace.php +++ /dev/null @@ -1,134 +0,0 @@ -configuration = $configuration; - $this->view = $view; - $this->dataProvider = $dataProvider; - $this->extConf = $extConf; - } - - public function renderWidgetContent(): string - { - try { - if ($this->extConf->getUsername() === '' || $this->extConf->getPassword() === '') { - $this->hasError = true; - $variables = [ - 'error' => 'No username or password given. Please configure authentication in extension settings of EXT:plesk_widget' - ]; - } else { - $variables = [ - 'configuration' => $this->configuration, - 'customer' => $this->dataProvider->getCustomer(), - 'hosting' => $this->dataProvider->getHosting(), - 'site' => $this->dataProvider->getSite(), - 'button' => $this->getButtonProvider() - ]; - } - } catch (\Exception $exception) { - $this->hasError = true; - $variables = [ - 'error' => $exception->getMessage() - ]; - } - - $this->view->setTemplate('Widget/Webspace'); - $this->view->assign('configuration', $this->configuration); - $this->view->assignMultiple($variables); - - return $this->view->render(); - } - - public function getEventData(): array - { - if ($this->hasError) { - $data = '{}'; - } else { - $data = $this->dataProvider->getChartData(); - } - - return [ - 'graphConfig' => [ - 'type' => 'doughnut', - 'options' => [ - //'maintainAspectRatio' => false, - 'legend' => [ - 'display' => true, - 'position' => 'bottom' - ], - 'tooltips' => [ - 'enabled' => true - ], - 'title' => [ - 'display' => true, - 'text' => 'Usage in ' . $this->extConf->getDiskUsageType() - ] - ], - 'data' => $data, - ], - ]; - } - - protected function getButtonProvider(): ButtonProvider - { - return GeneralUtility::makeInstance( - ButtonProvider::class, - 'Login to Plesk', - $this->dataProvider->getLoginLink(), - '_blank' - ); - } - - public function getCssFiles(): array - { - return [ - 'EXT:dashboard/Resources/Public/Css/Contrib/chart.css' - ]; - } - - public function getRequireJsModules(): array - { - return [ - 'TYPO3/CMS/Dashboard/Contrib/chartjs', - 'TYPO3/CMS/Dashboard/ChartInitializer', - ]; - } -} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 0d7a146..848e1ec 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -6,12 +6,12 @@ services: StefanFroemken\PleskWidget\: resource: '../Classes/*' + exclude: '../Classes/Widget/*' dashboard.widget.plesk.webspace: - class: 'StefanFroemken\PleskWidget\Widgets\Webspace' + class: 'StefanFroemken\PleskWidget\Widget\WebspaceWidget' arguments: - $view: '@dashboard.views.widget' - $dataProvider: '@StefanFroemken\PleskWidget\DataProvider\PleskDataProvider' + $dataProvider: '@StefanFroemken\PleskWidget\DataProvider\WebspaceDataProvider' tags: - name: dashboard.widget identifier: 'plesk.webspace' @@ -20,4 +20,31 @@ services: description: 'Show used and available diskspace' iconIdentifier: 'content-widget-rss' height: 'medium' - width: 'medium' + + dashboard.widget.plesk.server: + class: 'StefanFroemken\PleskWidget\Widget\ServerWidget' + arguments: + $dataProvider: '@StefanFroemken\PleskWidget\DataProvider\ServerDataProvider' + tags: + - name: dashboard.widget + identifier: 'plesk.server' + groupNames: 'systemInfo' + title: 'Plesk Server Information' + description: 'Show server related information and login link' + iconIdentifier: 'content-widget-rss' + height: 'medium' + + dashboard.widget.plesk.php: + class: 'StefanFroemken\PleskWidget\Widget\PhpWidget' + tags: + - name: dashboard.widget + identifier: 'plesk.php' + groupNames: 'systemInfo' + title: 'Plesk PHP Settings' + description: 'Show PHP related information' + iconIdentifier: 'content-widget-rss' + height: 'medium' + + StefanFroemken\PleskWidget\Service\PleskSiteService: + arguments: + $cache: '@cache.runtime' diff --git a/Configuration/page.tsconfig b/Configuration/page.tsconfig new file mode 100644 index 0000000..3ba23c1 --- /dev/null +++ b/Configuration/page.tsconfig @@ -0,0 +1 @@ +templates.typo3/cms-dashboard.1736608068 = stefanfroemken/plesk-widget:Resources/Private diff --git a/Resources/Private/Templates/Dashboard/Widget/Webspace.html b/Resources/Private/Templates/Dashboard/Widget/Webspace.html deleted file mode 100644 index 9681b75..0000000 --- a/Resources/Private/Templates/Dashboard/Widget/Webspace.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - -
- - -
{error}
-
- -
-
- -
-
-
-
-
Owner
-
{customer.personalName}
-
-
-
IP Address
- -
{ipAddress}
-
-
-
-
Hosting
-
PHP Handler: {hosting.php_handler_id}
-
PHP memory: {hosting.memory_limit}
-
PHP exec. time: {hosting.max_execution_time}
-
PHP max. upload: {hosting.upload_max_filesize}
-
-
-
-
-
-
- - - - - diff --git a/Resources/Private/Templates/Widget/Php.html b/Resources/Private/Templates/Widget/Php.html new file mode 100644 index 0000000..246d120 --- /dev/null +++ b/Resources/Private/Templates/Widget/Php.html @@ -0,0 +1,39 @@ + + + + + + + {error} + +

{domain}

+
+
max_execution_time
+
{hosting.properties.max_execution_time}
+
+
+
open_basedir
+
{hosting.properties.open_basedir}
+
+
+
memory_limit
+
{hosting.properties.memory_limit}
+
+
+
post_max_size
+
{hosting.properties.post_max_size}
+
+
+
upload_max_filesize
+
{hosting.properties.upload_max_filesize}
+
+
+
+
+ + + + + diff --git a/Resources/Private/Templates/Widget/Server.html b/Resources/Private/Templates/Widget/Server.html new file mode 100644 index 0000000..ec78e5d --- /dev/null +++ b/Resources/Private/Templates/Widget/Server.html @@ -0,0 +1,32 @@ + + + + + + + {error} + +
+
Owner
+
{customer.personalName}
+
+
+
Server IP Address
+ +
{ipAddress}
+
+
+ +
Public IP Address
+
{externalIpAddress}
+
+
+
+
+ + + + + diff --git a/Resources/Private/Templates/Widget/Webspace.html b/Resources/Private/Templates/Widget/Webspace.html new file mode 100644 index 0000000..043c825 --- /dev/null +++ b/Resources/Private/Templates/Widget/Webspace.html @@ -0,0 +1,22 @@ + + + + + + + {error} + + +
+ +
+
+
+
+ + + + + diff --git a/composer.json b/composer.json index c06da0a..4f09f86 100644 --- a/composer.json +++ b/composer.json @@ -18,9 +18,8 @@ "source": "https://github.com/froemken/plesk_widget" }, "require": { - "php": "^7.4 || ^8.0", - "typo3/cms-core": "^11.5.41 || ^12.4.24", - "typo3/cms-dashboard": "^11.5.41 || ^12.4.24", + "typo3/cms-core": "^12.4.24", + "typo3/cms-dashboard": "^12.4.24", "plesk/api-php-lib": "1.1.2" }, "require-dev": { diff --git a/ext_conf_template.txt b/ext_conf_template.txt index f67fe05..05676b5 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -1,10 +1,12 @@ -# cat=basic; type=options[Percent (%)=%,MegaByte (MB)=MB,GigaByte (GB)=GB]; label=Disk usage type: Show disk usage in percent or in Mega- or GigaByte? -diskUsageType = % -# cat=basic; type=string; label=Plesk host: Insert without scheme, port or path +# cat=plesk; type=string; label=Plesk host: Insert without scheme, port or path host = -# cat=basic; type=int; label=Plesk port: Plesk default port is 8443 +# cat=plesk; type=int; label=Plesk port: Plesk default port is 8443 port = 8443 -# cat=basic; type=string; label=Plesk username +# cat=plesk; type=string; label=Plesk username username = -# cat=basic; type=string; label=Plesk password +# cat=plesk; type=string; label=Plesk password password = +# cat=basic; type=options[Percent (%)=%,MegaByte (MB)=MB,GigaByte (GB)=GB]; label=Disk usage type: Show disk usage in percent or in Mega- or GigaByte? +diskUsageType = % +# cat=basic; type=string; label=Domain: Enter domain name to show info about. Without www and without scheme (https://) +domain = diff --git a/ext_emconf.php b/ext_emconf.php index c9e3490..e85fedf 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -9,11 +9,11 @@ 'author_company' => '', 'state' => 'stable', 'clearCacheOnLoad' => 0, - 'version' => '1.2.1', + 'version' => '2.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '11.5.41-12.4.99', - 'dashboard' => '11.5.41-12.4.99', + 'typo3' => '12.4.24', + 'dashboard' => '12.4.24', ], 'conflicts' => [ ], From 24c7c6f9f8e6aa3696aad9f5b395d55dc1193b1d Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 20:45:08 +0100 Subject: [PATCH 02/11] Update testing environment --- .github/workflows/ci.yml | 56 +-- Build/FunctionalTests.xml | 28 ++ Build/FunctionalTestsBootstrap.php | 17 + Build/Scripts/runTests.sh | 395 +++++++++++++++++ Build/UnitTests.xml | 29 ++ Build/UnitTestsBootstrap.php | 75 ++++ .../{.php_cs.php => php-cs-fixer/config.php} | 56 +-- Build/testing-docker/docker-compose.yml | 398 ++++++++++++++++++ composer.json | 33 +- 9 files changed, 999 insertions(+), 88 deletions(-) create mode 100644 Build/FunctionalTests.xml create mode 100644 Build/FunctionalTestsBootstrap.php create mode 100755 Build/Scripts/runTests.sh create mode 100644 Build/UnitTests.xml create mode 100644 Build/UnitTestsBootstrap.php rename Build/{.php_cs.php => php-cs-fixer/config.php} (66%) create mode 100644 Build/testing-docker/docker-compose.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5891ce5..46cc7db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,54 +1,22 @@ -# Adapted from https://github.com/TYPO3GmbH/blog/blob/master/.github/workflows/ci.yml -name: CI +name: Tests -on: [pull_request] +on: pull_request jobs: - build: - runs-on: ubuntu-latest + CGL: + name: Coding Style Check (TYPO3 Community CGL) - strategy: - fail-fast: false - matrix: - typo3: [^10.4, ^11.5] - php: ['7.4'] - include: - - typo3: ^11.5 - php: '8.0' - - typo3: ^11.5 - php: '8.1' + runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 - - - name: Setup PHP ${{ matrix.php }}, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mbstring, dom, zip - - - name: Validate composer.json and composer.lock - run: composer validate + uses: actions/checkout@v4 - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install dependencies with typo3/cms-core:${{ matrix.typo3 }} - run: | - composer require typo3/cms-core:${{ matrix.typo3 }} --no-progress - git checkout composer.json - - - name: php-cs-fixer - run: composer ci:php:fixer + - name: Composer + run: Build/Scripts/runTests.sh -t 12 -p 8.1 -s composerUpdate - name: Lint PHP - run: composer ci:php:lint + run: Build/Scripts/runTests.sh -t 12 -p 8.1 -s lint + + - name: Validate code against CGL + run: Build/Scripts/runTests.sh -t 12 -p 8.1 -s cgl -n diff --git a/Build/FunctionalTests.xml b/Build/FunctionalTests.xml new file mode 100644 index 0000000..59a41b6 --- /dev/null +++ b/Build/FunctionalTests.xml @@ -0,0 +1,28 @@ + + + + ../Tests/Functional/ + + + + + + + diff --git a/Build/FunctionalTestsBootstrap.php b/Build/FunctionalTestsBootstrap.php new file mode 100644 index 0000000..4040cac --- /dev/null +++ b/Build/FunctionalTestsBootstrap.php @@ -0,0 +1,17 @@ +defineOriginalRootPath(); + $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests'); + $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/transient'); +}); diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh new file mode 100755 index 0000000..3d719c3 --- /dev/null +++ b/Build/Scripts/runTests.sh @@ -0,0 +1,395 @@ +#!/usr/bin/env bash + +# +# TYPO3 core test runner based on docker and docker-compose. +# + +# Function to write a .env file in Build/testing-docker +# This is read by docker-compose and vars defined here are +# used in Build/testing-docker/docker-compose.yml +setUpDockerComposeDotEnv() { + # Delete possibly existing local .env file if exists + [ -e .env ] && rm .env + # Set up a new .env file for docker-compose + { + echo "COMPOSE_PROJECT_NAME=local" + # To prevent access rights of files created by the testing, the docker image later + # runs with the same user that is currently executing the script. docker-compose can't + # use $UID directly itself since it is a shell variable and not an env variable, so + # we have to set it explicitly here. + echo "HOST_UID=`id -u`" + # Your local user + echo "ROOT_DIR=${ROOT_DIR}" + echo "HOST_USER=${USER}" + echo "TEST_FILE=${TEST_FILE}" + echo "TYPO3_VERSION=${TYPO3_VERSION}" + echo "PHP_XDEBUG_ON=${PHP_XDEBUG_ON}" + echo "PHP_XDEBUG_PORT=${PHP_XDEBUG_PORT}" + echo "DOCKER_PHP_IMAGE=${DOCKER_PHP_IMAGE}" + echo "EXTRA_TEST_OPTIONS=${EXTRA_TEST_OPTIONS}" + echo "SCRIPT_VERBOSE=${SCRIPT_VERBOSE}" + echo "CGLCHECK_DRY_RUN=${CGLCHECK_DRY_RUN}" + echo "DATABASE_DRIVER=${DATABASE_DRIVER}" + } > .env +} + +# Options -a and -d depend on each other. The function +# validates input combinations and sets defaults. +handleDbmsAndDriverOptions() { + case ${DBMS} in + mysql|mariadb) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid option -a ${DATABASE_DRIVER} with -d ${DBMS}" >&2 + echo >&2 + echo "call \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres|sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid option -a ${DATABASE_DRIVER} with -d ${DBMS}" >&2 + echo >&2 + echo "call \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + esac +} + +# Load help text into $HELP +read -r -d '' HELP <=20.10 for xdebug break pointing to work reliably, and +a recent docker-compose (tested >=1.21.2) is needed. + +Usage: $0 [options] [file] + +No arguments: Run all unit tests with PHP 8.1 + +Options: + -s <...> + Specifies which test suite to run + - acceptance: backend acceptance tests + - cgl: cgl test and fix all php files + - clean: clean up build and testing related files + - composerUpdate: "composer update", handy if host has no PHP + - functional: functional tests + - lint: PHP linting + - phpstan: phpstan analyze + - phpstanGenerateBaseline: regenerate phpstan baseline, handy after phpstan updates + - unit (default): PHP unit tests + + -a + Only with -s acceptance,functional + Specifies to use another driver, following combinations are available: + - mysql + - mysqli (default) + - pdo_mysql + - mariadb + - mysqli (default) + - pdo_mysql + + -d + Only with -s acceptance,functional + Specifies on which DBMS tests are performed + - sqlite: (default) use sqlite + - mariadb: use mariadb + - mysql: use mysql + - postgres: use postgres + + + -p <7.4|8.0|8.1|8.2> + Specifies the PHP minor version to be used + - 7.4 (default): use PHP 7.4 + - 8.0: use PHP 8.0 + - 8.1: use PHP 8.1 + - 8.2: use PHP 8.2 + + -t <11|12> + Only with -s composerUpdate + Specifies the TYPO3 core major version to be used + - 11 (default): use TYPO3 core v11 + - 12: Use TYPO3 core v12 + + -e "" + Only with -s acceptance|functional|unit + Additional options to send to phpunit (unit & functional tests) or codeception (acceptance + tests). For phpunit, options starting with "--" must be added after options starting with "-". + Example -e "-v --filter canRetrieveValueWithGP" to enable verbose output AND filter tests + named "canRetrieveValueWithGP" + + -x + Only with -s functional|unit|acceptance + Send information to host instance for test or system under test break points. This is especially + useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port + can be selected with -y + + -n + Only with -s cgl + Activate dry-run in CGL check that does not actively change files and only prints broken ones. + + -u + Update existing typo3/core-testing-*:latest docker images. Maintenance call to docker pull latest + versions of the main php images. The images are updated once in a while and only the youngest + ones are supported by core testing. Use this if weird test errors occur. Also removes obsolete + image versions of typo3/core-testing-*. + + -v + Enable verbose script output. Shows variables and docker commands. + + -h + Show this help. + +Examples: + # Run unit tests using PHP 7.4 + ./Build/Scripts/runTests.sh +EOF + +# Test if docker-compose exists, else exit out with error +if ! type "docker-compose" > /dev/null; then + echo "This script relies on docker and docker-compose. Please install" >&2 + exit 1 +fi + +# Go to the directory this script is located, so everything else is relative +# to this dir, no matter from where this script is called. +THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +cd "$THIS_SCRIPT_DIR" || exit 1 + +# Go to directory that contains the local docker-compose.yml file +cd ../testing-docker || exit 1 + +# Option defaults +if ! command -v realpath &> /dev/null; then + echo "This script works best with realpath installed" >&2 + ROOT_DIR="${PWD}/../../" +else + ROOT_DIR=`realpath ${PWD}/../../` +fi +TEST_SUITE="unit" +DBMS="sqlite" +PHP_VERSION="7.4" +TYPO3_VERSION="11" +PHP_XDEBUG_ON=0 +PHP_XDEBUG_PORT=9003 +EXTRA_TEST_OPTIONS="" +SCRIPT_VERBOSE=0 +CGLCHECK_DRY_RUN="" +DATABASE_DRIVER="" + +# Option parsing +# Reset in case getopts has been used previously in the shell +OPTIND=1 +# Array for invalid options +INVALID_OPTIONS=(); +# Simple option parsing based on getopts (! not getopt) +while getopts ":s:a:d:p:t:e:xnhuv" OPT; do + case ${OPT} in + s) + TEST_SUITE=${OPTARG} + ;; + a) + DATABASE_DRIVER=${OPTARG} + ;; + d) + DBMS=${OPTARG} + ;; + p) + PHP_VERSION=${OPTARG} + if ! [[ ${PHP_VERSION} =~ ^(7.4|8.0|8.1|8.2)$ ]]; then + INVALID_OPTIONS+=("p ${OPTARG}") + fi + ;; + t) + TYPO3_VERSION=${OPTARG} + if ! [[ ${TYPO3_VERSION} =~ ^(11|12)$ ]]; then + INVALID_OPTIONS+=("p ${OPTARG}") + fi + ;; + e) + EXTRA_TEST_OPTIONS=${OPTARG} + ;; + x) + PHP_XDEBUG_ON=1 + ;; + y) + PHP_XDEBUG_PORT=${OPTARG} + ;; + h) + echo "${HELP}" + exit 0 + ;; + n) + CGLCHECK_DRY_RUN="-n" + ;; + u) + TEST_SUITE=update + ;; + v) + SCRIPT_VERBOSE=1 + ;; + \?) + INVALID_OPTIONS+=(${OPTARG}) + ;; + :) + INVALID_OPTIONS+=(${OPTARG}) + ;; + esac +done + +# Exit on invalid options +if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then + echo "Invalid option(s):" >&2 + for I in "${INVALID_OPTIONS[@]}"; do + echo "-"${I} >&2 + done + echo >&2 + echo "${HELP}" >&2 + exit 1 +fi + +# Move "7.4" to "php74", the latter is the docker container name +DOCKER_PHP_IMAGE=`echo "php${PHP_VERSION}" | sed -e 's/\.//'` + +# Set $1 to first mass argument, this is the optional test file or test directory to execute +shift $((OPTIND - 1)) +TEST_FILE=${1} + +if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x +fi + +# Suite execution +case ${TEST_SUITE} in + acceptance) + handleDbmsAndDriverOptions + setUpDockerComposeDotEnv + case ${DBMS} in + sqlite) + echo "Using driver: ${DATABASE_DRIVER}" + docker-compose run acceptance_cli_sqlite + SUITE_EXIT_CODE=$? + ;; + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + docker-compose run acceptance_cli_mysql80 + SUITE_EXIT_CODE=$? + ;; + mariadb) + echo "Using driver: ${DATABASE_DRIVER}" + docker-compose run acceptance_cli_mariadb10 + SUITE_EXIT_CODE=$? + ;; + postgres) + docker-compose run acceptance_cli_postgres10 + SUITE_EXIT_CODE=$? + ;; + *) + echo "Acceptance tests don't run with DBMS ${DBMS}" >&2 + echo >&2 + echo "call \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + esac + docker-compose down + ;; + cgl) + # Active dry-run for cgl needs not "-n" but specific options + if [[ ! -z ${CGLCHECK_DRY_RUN} ]]; then + CGLCHECK_DRY_RUN="--dry-run --diff" + fi + setUpDockerComposeDotEnv + docker-compose run cgl + SUITE_EXIT_CODE=$? + docker-compose down + ;; + clean) + rm -rf ../../composer.lock ../../.Build/ ../../Tests/Acceptance/Support/_generated/ ../../composer.json.testing + ;; + composerUpdate) + setUpDockerComposeDotEnv + cp ../../composer.json ../../composer.json.orig + if [ -f "../../composer.json.testing" ]; then + cp ../../composer.json ../../composer.json.orig + fi + docker-compose run composer_update + cp ../../composer.json ../../composer.json.testing + mv ../../composer.json.orig ../../composer.json + SUITE_EXIT_CODE=$? + docker-compose down + ;; + functional) + handleDbmsAndDriverOptions + setUpDockerComposeDotEnv + case ${DBMS} in + mariadb) + echo "Using driver: ${DATABASE_DRIVER}" + docker-compose run functional_mariadb10 + SUITE_EXIT_CODE=$? + ;; + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + docker-compose run functional_mysql80 + SUITE_EXIT_CODE=$? + ;; + postgres) + docker-compose run functional_postgres10 + SUITE_EXIT_CODE=$? + ;; + sqlite) + # sqlite has a tmpfs as Web/typo3temp/var/tests/functional-sqlite-dbs/ + # Since docker is executed as root (yay!), the path to this dir is owned by + # root if docker creates it. Thank you, docker. We create the path beforehand + # to avoid permission issues. + mkdir -p ${ROOT_DIR}/Web/typo3temp/var/tests/functional-sqlite-dbs/ + docker-compose run functional_sqlite + SUITE_EXIT_CODE=$? + ;; + *) + echo "Invalid -d option argument ${DBMS}" >&2 + echo >&2 + echo "${HELP}" >&2 + exit 1 + esac + docker-compose down + ;; + lint) + setUpDockerComposeDotEnv + docker-compose run lint + SUITE_EXIT_CODE=$? + docker-compose down + ;; + phpstan) + setUpDockerComposeDotEnv + docker-compose run phpstan + SUITE_EXIT_CODE=$? + docker-compose down + ;; + phpstanGenerateBaseline) + setUpDockerComposeDotEnv + docker-compose run phpstan_generate_baseline + SUITE_EXIT_CODE=$? + docker-compose down + ;; + unit) + setUpDockerComposeDotEnv + docker-compose run unit + SUITE_EXIT_CODE=$? + docker-compose down + ;; + update) + # pull typo3/core-testing-*:latest versions of those ones that exist locally + docker images typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} docker pull {} + # remove "dangling" typo3/core-testing-* images (those tagged as ) + docker images typo3/core-testing-* --filter "dangling=true" --format "{{.ID}}" | xargs -I {} docker rmi {} + ;; + *) + echo "Invalid -s option argument ${TEST_SUITE}" >&2 + echo >&2 + echo "${HELP}" >&2 + exit 1 +esac + +exit $SUITE_EXIT_CODE diff --git a/Build/UnitTests.xml b/Build/UnitTests.xml new file mode 100644 index 0000000..a90da4c --- /dev/null +++ b/Build/UnitTests.xml @@ -0,0 +1,29 @@ + + + + ../Tests/Unit/ + + + + + + + diff --git a/Build/UnitTestsBootstrap.php b/Build/UnitTestsBootstrap.php new file mode 100644 index 0000000..ed750f5 --- /dev/null +++ b/Build/UnitTestsBootstrap.php @@ -0,0 +1,75 @@ +getWebRoot(), '/')); + } + if (!getenv('TYPO3_PATH_WEB')) { + putenv('TYPO3_PATH_WEB=' . rtrim($testbase->getWebRoot(), '/')); + } + + $testbase->defineSitePath(); + + // We can use the "typo3/cms-composer-installers" constant "TYPO3_COMPOSER_MODE" to determine composer mode. + // This should be always true except for TYPO3 mono repository. + $composerMode = defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE === true; + $requestType = \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_BE | \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI; + SystemEnvironmentBuilder::run(0, $requestType, $composerMode); + + $testbase->createDirectory(Environment::getPublicPath() . '/typo3conf/ext'); + $testbase->createDirectory(Environment::getPublicPath() . '/typo3temp/assets'); + $testbase->createDirectory(Environment::getPublicPath() . '/typo3temp/var/tests'); + $testbase->createDirectory(Environment::getPublicPath() . '/typo3temp/var/transient'); + + // Retrieve an instance of class loader and inject to core bootstrap + $classLoader = require $testbase->getPackagesPath() . '/autoload.php'; + Bootstrap::initializeClassLoader($classLoader); + + // Initialize default TYPO3_CONF_VARS + $configurationManager = new ConfigurationManager(); + $GLOBALS['TYPO3_CONF_VARS'] = $configurationManager->getDefaultConfiguration(); + + $cache = new PhpFrontend( + 'core', + new NullBackend('production', []) + ); + $packageManager = Bootstrap::createPackageManager( + UnitTestPackageManager::class, + Bootstrap::createPackageCache($cache) + ); + + GeneralUtility::setSingletonInstance(PackageManager::class, $packageManager); + ExtensionManagementUtility::setPackageManager($packageManager); + + $testbase->dumpClassLoadingInformation(); + + GeneralUtility::purgeInstances(); +})(); diff --git a/Build/.php_cs.php b/Build/php-cs-fixer/config.php similarity index 66% rename from Build/.php_cs.php rename to Build/php-cs-fixer/config.php index cb8f19e..633c646 100644 --- a/Build/.php_cs.php +++ b/Build/php-cs-fixer/config.php @@ -1,47 +1,43 @@ name('*.php') ->exclude('.build') - ->exclude('var') ->in(__DIR__); -$config = new PhpCsFixer\Config(); -return $config +return (new Config()) + ->setFinder($finder) ->setRiskyAllowed(true) ->setRules([ '@DoctrineAnnotation' => true, - '@PSR2' => true, + '@PER' => true, 'header_comment' => [ - 'header' => $headerComment + 'header' => $headerComment, ], + 'no_superfluous_phpdoc_tags' => true, 'array_syntax' => ['syntax' => 'short'], 'blank_line_after_opening_tag' => true, 'braces' => ['allow_single_line_closure' => true], @@ -50,9 +46,11 @@ 'concat_space' => ['spacing' => 'one'], 'declare_equal_normalize' => ['space' => 'none'], 'dir_constant' => true, + 'function_to_constant' => ['functions' => ['get_called_class', 'get_class', 'get_class_this', 'php_sapi_name', 'phpversion', 'pi']], 'function_typehint_space' => true, 'lowercase_cast' => true, 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'], + 'modernize_strpos' => true, 'modernize_types_casting' => true, 'native_function_casing' => true, 'new_with_braces' => true, @@ -67,13 +65,14 @@ 'no_short_bool_cast' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_superfluous_elseif' => true, - 'no_trailing_comma_in_singleline_array' => true, + 'no_trailing_comma_in_singleline' => true, 'no_unneeded_control_parentheses' => true, 'no_unused_imports' => true, 'no_useless_else' => true, + 'no_useless_nullsafe_operator' => true, 'no_whitespace_in_blank_line' => true, 'ordered_imports' => true, - 'php_unit_construct' => true, + 'php_unit_construct' => ['assertions' => ['assertEquals', 'assertSame', 'assertNotEquals', 'assertNotSame']], 'php_unit_mock_short_will_return' => true, 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], 'phpdoc_no_access' => true, @@ -84,9 +83,10 @@ 'phpdoc_types' => true, 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], 'return_type_declaration' => ['space_before' => 'none'], - 'single_line_comment_style' => false, 'single_quote' => true, + 'single_line_comment_style' => ['comment_types' => ['hash']], 'single_trait_insert_per_statement' => true, - 'whitespace_after_comma_in_array' => true - ]) - ->setFinder($finder); + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + 'whitespace_after_comma_in_array' => ['ensure_single_space' => true], + 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], + ]); diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml new file mode 100644 index 0000000..692b5ac --- /dev/null +++ b/Build/testing-docker/docker-compose.yml @@ -0,0 +1,398 @@ +version: '2.3' +services: + mariadb10: + # not using mariadb:10 for the time being, because 10.5.7 (currently latest) is broken + image: mariadb:10.5.6 + environment: + MYSQL_ROOT_PASSWORD: funcp + tmpfs: + - /var/lib/mysql/:rw,noexec,nosuid + + mysql80: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: funcp + tmpfs: + - /var/lib/mysql/:rw,noexec,nosuid + + postgres10: + image: postgres:10-alpine + environment: + POSTGRES_PASSWORD: funcp + POSTGRES_USER: ${HOST_USER} + tmpfs: + - /var/lib/postgresql/data:rw,noexec,nosuid + + acceptance_cli_mariadb10: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + links: + - mariadb10 + environment: + typo3DatabaseDriver: "${DATABASE_DRIVER}" + typo3DatabaseName: func_test + typo3DatabaseUsername: root + typo3DatabasePassword: funcp + typo3DatabaseHost: mariadb10 + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR}/ + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + echo Waiting for database start...; + while ! nc -z mariadb10 3306; do + sleep 1; + done; + echo Database is up; + php -v | grep '^PHP'; + mkdir -p .Build/Web/typo3temp/var/tests/ + COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" $${COMMAND}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; + fi + " + + acceptance_cli_mysql80: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + links: + - mysql80 + environment: + typo3DatabaseDriver: "${DATABASE_DRIVER}" + typo3DatabaseName: func_test + typo3DatabaseUsername: root + typo3DatabasePassword: funcp + typo3DatabaseHost: mysql80 + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + echo Waiting for database start...; + while ! nc -z mysql80 3306; do + sleep 1; + done; + echo Database is up; + php -v | grep '^PHP'; + mkdir -p .Build/Web/typo3temp/var/tests/ + COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" $${COMMAND}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; + fi + " + + acceptance_cli_postgres10: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + links: + - postgres10 + environment: + typo3DatabaseDriver: pdo_pgsql + typo3DatabaseName: bamboo + typo3DatabaseUsername: ${HOST_USER} + typo3DatabaseHost: postgres10 + typo3DatabasePassword: funcp + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + echo Waiting for database start...; + while ! nc -z postgres10 5432; do + sleep 1; + done; + echo Database is up; + php -v | grep '^PHP'; + mkdir -p .Build/Web/typo3temp/var/tests/ + COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" $${COMMAND}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; + fi + " + + acceptance_cli_sqlite: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + environment: + typo3DatabaseDriver: pdo_sqlite + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + php -v | grep '^PHP'; + mkdir -p Web/typo3temp/var/tests/ + COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" $${COMMAND}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; + fi + " + + cgl: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + php -v | grep '^PHP'; + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --config=Build/php-cs-fixer/config.php + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" PHP_CS_FIXER_ALLOW_XDEBUG=1 .Build/bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --config=Build/php-cs-fixer/config.php + fi + " + + composer_update: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + environment: + COMPOSER_CACHE_DIR: ".cache/composer" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + php -v | grep '^PHP'; + if [ ${TYPO3_VERSION} -eq 11 ]; then + composer req --dev --no-update typo3/cms-composer-installers:^3.0 typo3/cms-workspaces:^11.5 typo3/cms-impexp:^11.5 + composer req typo3/cms-core:^11.5 --no-update + fi + if [ ${TYPO3_VERSION} -eq 12 ]; then + composer req --dev --no-update "typo3/cms-composer-installers:^5.0" typo3/cms-impexp:^12.4 typo3/cms-workspaces:^12.4 + composer req typo3/cms-core:^12.4 --no-update + fi + composer update --no-progress --no-interaction; + " + + functional_mariadb10: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + links: + - mariadb10 + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + environment: + typo3DatabaseDriver: "${DATABASE_DRIVER}" + typo3DatabaseName: func_test + typo3DatabaseUsername: root + typo3DatabasePassword: funcp + typo3DatabaseHost: mariadb10 + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + echo Waiting for database start...; + while ! nc -z mariadb10 3306; do + sleep 1; + done; + echo Database is up; + php -v | grep '^PHP'; + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + php -dxdebug.mode=off .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + else + php -dxdebug.mode='debug,develop' XDEBUG_TRIGGER='foo' XDEBUG_CONFIG='client_host=host.docker.internal' .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + fi + " + + functional_mysql80: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + links: + - mysql80 + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + environment: + typo3DatabaseDriver: "${DATABASE_DRIVER}" + typo3DatabaseName: func_test + typo3DatabaseUsername: root + typo3DatabasePassword: funcp + typo3DatabaseHost: mysql80 + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + echo Waiting for database start...; + while ! nc -z mysql80 3306; do + sleep 1; + done; + echo Database is up; + php -v | grep '^PHP'; + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + fi + " + + functional_postgres10: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + links: + - postgres10 + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + environment: + typo3DatabaseDriver: pdo_pgsql + typo3DatabaseName: bamboo + typo3DatabaseUsername: ${HOST_USER} + typo3DatabaseHost: postgres10 + typo3DatabasePassword: funcp + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + echo Waiting for database start...; + while ! nc -z postgres10 5432; do + sleep 1; + done; + echo Database is up; + php -v | grep '^PHP'; + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; + fi + " + + functional_sqlite: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + tmpfs: + - ${ROOT_DIR}/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid,uid=${HOST_UID} + environment: + typo3DatabaseDriver: pdo_sqlite + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + php -v | grep '^PHP'; + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; + fi + " + + lint: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + php -v | grep '^PHP'; + find . -name \\*.php ! -path "./.Build/\\*" -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null + " + + phpstan: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + mkdir -p .Build/.cache + php -v | grep '^PHP'; + php -dxdebug.mode=off .Build/bin/phpstan analyze -c Build/phpstan.neon --no-progress + " + + phpstan_generate_baseline: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + mkdir -p .Build/.cache + php -v | grep '^PHP'; + php -dxdebug.mode=off .Build/bin/phpstan analyze -c Build/phpstan.neon --generate-baseline=Build/phpstan-baseline.neon + " + + unit: + image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest + user: "${HOST_UID}" + volumes: + - ${ROOT_DIR}:${ROOT_DIR} + working_dir: ${ROOT_DIR} + extra_hosts: + - "host.docker.internal:host-gateway" + command: > + /bin/sh -c " + if [ ${SCRIPT_VERBOSE} -eq 1 ]; then + set -x + fi + php -v | grep '^PHP'; + if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + else + XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; + fi + " \ No newline at end of file diff --git a/composer.json b/composer.json index 4f09f86..871ee1c 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,14 @@ "plesk/api-php-lib": "1.1.2" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.4" + "friendsofphp/php-cs-fixer": "^3.14", + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest", + "typo3/coding-standards": "^0.6", + "typo3/testing-framework": "^7.0.2" + }, + "replace": { + "typo3-ter/plesk-widget": "self.version" }, "autoload": { "psr-4": { @@ -32,25 +39,19 @@ } }, "config": { - "vendor-dir": ".build/vendor" + "sort-packages": true, + "vendor-dir": ".Build/vendor", + "bin-dir": ".Build/bin", + "allow-plugins": { + "typo3/class-alias-loader": true, + "typo3/cms-composer-installers": true + } }, "extra": { "typo3/cms": { "extension-key": "plesk_widget", - "app-dir": ".build", - "web-dir": ".build/public" + "app-dir": ".Build", + "web-dir": ".Build/public" } - }, - "scripts": { - "php:fix": ".build/vendor/bin/php-cs-fixer --config=Build/.php_cs.php fix Classes", - "ci:php:lint": "find *.php Classes Configuration -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l", - "ci:php:fixer": ".build/vendor/bin/php-cs-fixer --config=Build/.php_cs.php fix --dry-run -v --show-progress=dots --diff Classes", - "link-extension": [ - "@php -r 'is_dir($extFolder=__DIR__.\"/.build/public/typo3conf/ext/\") || mkdir($extFolder, 0777, true);'", - "@php -r 'file_exists($extFolder=__DIR__.\"/.build/public/typo3conf/ext/plesk_widget\") || symlink(__DIR__,$extFolder);'" - ], - "post-autoload-dump": [ - "@link-extension" - ] } } From c3e00569a651e7fd4f4a73de6216f086864f6af5 Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 20:49:25 +0100 Subject: [PATCH 03/11] Update runScript --- Build/Scripts/runTests.sh | 647 +++++++++++++++++++++++++------------- 1 file changed, 429 insertions(+), 218 deletions(-) diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 3d719c3..e8f2f11 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -1,89 +1,164 @@ #!/usr/bin/env bash # -# TYPO3 core test runner based on docker and docker-compose. +# EXT:examples test runner based on docker/podman. # -# Function to write a .env file in Build/testing-docker -# This is read by docker-compose and vars defined here are -# used in Build/testing-docker/docker-compose.yml -setUpDockerComposeDotEnv() { - # Delete possibly existing local .env file if exists - [ -e .env ] && rm .env - # Set up a new .env file for docker-compose - { - echo "COMPOSE_PROJECT_NAME=local" - # To prevent access rights of files created by the testing, the docker image later - # runs with the same user that is currently executing the script. docker-compose can't - # use $UID directly itself since it is a shell variable and not an env variable, so - # we have to set it explicitly here. - echo "HOST_UID=`id -u`" - # Your local user - echo "ROOT_DIR=${ROOT_DIR}" - echo "HOST_USER=${USER}" - echo "TEST_FILE=${TEST_FILE}" - echo "TYPO3_VERSION=${TYPO3_VERSION}" - echo "PHP_XDEBUG_ON=${PHP_XDEBUG_ON}" - echo "PHP_XDEBUG_PORT=${PHP_XDEBUG_PORT}" - echo "DOCKER_PHP_IMAGE=${DOCKER_PHP_IMAGE}" - echo "EXTRA_TEST_OPTIONS=${EXTRA_TEST_OPTIONS}" - echo "SCRIPT_VERBOSE=${SCRIPT_VERBOSE}" - echo "CGLCHECK_DRY_RUN=${CGLCHECK_DRY_RUN}" - echo "DATABASE_DRIVER=${DATABASE_DRIVER}" - } > .env +trap 'cleanUp;exit 2' SIGINT + +waitFor() { + local HOST=${1} + local PORT=${2} + local TESTCOMMAND=" + COUNT=0; + while ! nc -z ${HOST} ${PORT}; do + if [ \"\${COUNT}\" -gt 10 ]; then + echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; + exit 1; + fi; + sleep 1; + COUNT=\$((COUNT + 1)); + done; + " + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" + if [[ $? -gt 0 ]]; then + kill -SIGINT -$$ + fi } -# Options -a and -d depend on each other. The function -# validates input combinations and sets defaults. -handleDbmsAndDriverOptions() { +cleanUp() { + ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}') + for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do + ${CONTAINER_BIN} rm -f ${ATTACHED_CONTAINER} >/dev/null + done + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null +} + +cleanCacheFiles() { + echo -n "Clean caches ... " + rm -rf \ + .Build/.cache \ + .php-cs-fixer.cache + echo "done" +} + +cleanRenderedDocumentationFiles() { + echo -n "Clean rendered documentation files ... " + rm -rf \ + Documentation-GENERATED-temp + echo "done" +} + +handleDbmsOptions() { + # -a, -d, -i depend on each other. Validate input combinations and set defaults. case ${DBMS} in - mysql|mariadb) + mariadb) [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then - echo "Invalid option -a ${DATABASE_DRIVER} with -d ${DBMS}" >&2 + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4" + if ! [[ ${DBMS_VERSION} =~ ^(10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 echo >&2 - echo "call \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 exit 1 fi ;; - postgres|sqlite) + mysql) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" + if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres) if [ -n "${DATABASE_DRIVER}" ]; then - echo "Invalid option -a ${DATABASE_DRIVER} with -d ${DBMS}" >&2 + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" + if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 echo >&2 - echo "call \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 exit 1 fi ;; + sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + if [ -n "${DBMS_VERSION}" ]; then + echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + *) + echo "Invalid option -d ${DBMS}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + ;; esac } -# Load help text into $HELP -read -r -d '' HELP <=20.10 for xdebug break pointing to work reliably, and -a recent docker-compose (tested >=1.21.2) is needed. +loadHelp() { + # Load help text into $HELP + read -r -d '' HELP < Specifies which test suite to run - - acceptance: backend acceptance tests - cgl: cgl test and fix all php files - - clean: clean up build and testing related files + - clean: Clean temporary files + - cleanCache: Clean cache folds for files. + - cleanRenderedDocumentation: Clean existing rendered documentation output. + - composer: "composer" with all remaining arguments dispatched. + - composerNormalize: "composer normalize" - composerUpdate: "composer update", handy if host has no PHP - - functional: functional tests + - composerUpdateRector: "composer update", for rector subdirectory + - composerValidate: "composer validate" + - functional: PHP functional tests - lint: PHP linting - - phpstan: phpstan analyze - - phpstanGenerateBaseline: regenerate phpstan baseline, handy after phpstan updates - - unit (default): PHP unit tests + - phpstan: PHPStan static analysis + - phpstanBaseline: Generate PHPStan baseline + - unit: PHP unit tests + - rector: Apply Rector rules + - renderDocumentation + - testRenderDocumentation + + -b + Container environment: + - docker + - podman + + If not specified, podman will be used if available. Otherwise, docker is used. -a - Only with -s acceptance,functional + Only with -s functional|functionalDeprecated Specifies to use another driver, following combinations are available: - mysql - mysqli (default) @@ -93,149 +168,155 @@ Options: - pdo_mysql -d - Only with -s acceptance,functional + Only with -s functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall Specifies on which DBMS tests are performed - - sqlite: (default) use sqlite + - sqlite: (default): use sqlite - mariadb: use mariadb - - mysql: use mysql + - mysql: use MySQL - postgres: use postgres + -i version + Specify a specific database version + With "-d mariadb": + - 10.4 short-term, maintained until 2024-06-18 (default) + - 10.5 short-term, maintained until 2025-06-24 + - 10.6 long-term, maintained until 2026-06 + - 10.7 short-term, no longer maintained + - 10.8 short-term, maintained until 2023-05 + - 10.9 short-term, maintained until 2023-08 + - 10.10 short-term, maintained until 2023-11 + - 10.11 long-term, maintained until 2028-02 + - 11.0 development series + - 11.1 short-term development series + With "-d mysql": + - 8.0 maintained until 2026-04 (default) LTS + - 8.1 unmaintained since 2023-10 + - 8.2 unmaintained since 2024-01 + - 8.3 maintained until 2024-04 + With "-d postgres": + - 10 unmaintained since 2022-11-10 (default) + - 11 unmaintained since 2023-11-09 + - 12 maintained until 2024-11-14 + - 13 maintained until 2025-11-13 + - 14 maintained until 2026-11-12 + - 15 maintained until 2027-11-11 + - 16 maintained until 2028-11-09 - -p <7.4|8.0|8.1|8.2> + -p <8.1|8.2|8.3> Specifies the PHP minor version to be used - - 7.4 (default): use PHP 7.4 - - 8.0: use PHP 8.0 - 8.1: use PHP 8.1 - 8.2: use PHP 8.2 - - -t <11|12> - Only with -s composerUpdate - Specifies the TYPO3 core major version to be used - - 11 (default): use TYPO3 core v11 - - 12: Use TYPO3 core v12 - - -e "" - Only with -s acceptance|functional|unit - Additional options to send to phpunit (unit & functional tests) or codeception (acceptance - tests). For phpunit, options starting with "--" must be added after options starting with "-". - Example -e "-v --filter canRetrieveValueWithGP" to enable verbose output AND filter tests - named "canRetrieveValueWithGP" + - 8.3: use PHP 8.3 -x - Only with -s functional|unit|acceptance + Only with -s functional|unit Send information to host instance for test or system under test break points. This is especially useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port can be selected with -y + -y + Send xdebug information to a different port than default 9003 if an IDE like PhpStorm + is not listening on default port. + -n - Only with -s cgl - Activate dry-run in CGL check that does not actively change files and only prints broken ones. + Only with -s cgl, composerNormalize, rector + Activate dry-run in CGL check and composer normalize that does not actively change files and only prints broken ones. -u - Update existing typo3/core-testing-*:latest docker images. Maintenance call to docker pull latest - versions of the main php images. The images are updated once in a while and only the youngest - ones are supported by core testing. Use this if weird test errors occur. Also removes obsolete - image versions of typo3/core-testing-*. - - -v - Enable verbose script output. Shows variables and docker commands. + Update existing typo3/core-testing-*:latest container images and remove dangling local volumes. + New images are published once in a while and only the latest ones are supported by core testing. + Use this if weird test errors occur. Also removes obsolete image versions of typo3/core-testing-*. -h Show this help. Examples: - # Run unit tests using PHP 7.4 - ./Build/Scripts/runTests.sh -EOF + # Run unit tests using PHP 8.2 + ./Build/Scripts/runTests.sh -p 8.2 -s unit -# Test if docker-compose exists, else exit out with error -if ! type "docker-compose" > /dev/null; then - echo "This script relies on docker and docker-compose. Please install" >&2 - exit 1 -fi + # Run functional tests using PHP 8.3 and MariaDB 10.6 using pdo_mysql + ./Build/Scripts/runTests.sh -p 8.3 -s functional -d mariadb -i 10.6 -a pdo_mysql -# Go to the directory this script is located, so everything else is relative -# to this dir, no matter from where this script is called. -THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -cd "$THIS_SCRIPT_DIR" || exit 1 + # Run functional tests on postgres with xdebug, php 8.3 and execute a restricted set of tests + ./Build/Scripts/runTests.sh -x -p 8.3 -s functional -d postgres -- Tests/Functional/DummyTest.php +EOF +} -# Go to directory that contains the local docker-compose.yml file -cd ../testing-docker || exit 1 +# Test if docker exists, else exit out with error +if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then + echo "This script relies on docker or podman. Please install" >&2 + exit 1 +fi # Option defaults -if ! command -v realpath &> /dev/null; then - echo "This script works best with realpath installed" >&2 - ROOT_DIR="${PWD}/../../" -else - ROOT_DIR=`realpath ${PWD}/../../` -fi -TEST_SUITE="unit" +# @todo Consider to switch from cgl to help as default +TEST_SUITE="cgl" +DATABASE_DRIVER="" DBMS="sqlite" -PHP_VERSION="7.4" -TYPO3_VERSION="11" +DBMS_VERSION="" +PHP_VERSION="8.1" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9003 -EXTRA_TEST_OPTIONS="" -SCRIPT_VERBOSE=0 -CGLCHECK_DRY_RUN="" -DATABASE_DRIVER="" +CGLCHECK_DRY_RUN=0 +CI_PARAMS="${CI_PARAMS:-}" +DOCS_PARAMS="${DOCS_PARAMS:=--pull always}" +CONTAINER_BIN="" +CONTAINER_HOST="host.docker.internal" -# Option parsing +# Option parsing updates above default vars # Reset in case getopts has been used previously in the shell OPTIND=1 # Array for invalid options -INVALID_OPTIONS=(); +INVALID_OPTIONS=() # Simple option parsing based on getopts (! not getopt) -while getopts ":s:a:d:p:t:e:xnhuv" OPT; do +while getopts "a:b:d:i:s:p:xy:nhu" OPT; do case ${OPT} in + a) + DATABASE_DRIVER=${OPTARG} + ;; s) TEST_SUITE=${OPTARG} ;; - a) - DATABASE_DRIVER=${OPTARG} + b) + if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then + INVALID_OPTIONS+=("${OPTARG}") + fi + CONTAINER_BIN=${OPTARG} ;; d) DBMS=${OPTARG} ;; + i) + DBMS_VERSION=${OPTARG} + ;; p) PHP_VERSION=${OPTARG} - if ! [[ ${PHP_VERSION} =~ ^(7.4|8.0|8.1|8.2)$ ]]; then + if ! [[ ${PHP_VERSION} =~ ^(8.1|8.2|8.3)$ ]]; then INVALID_OPTIONS+=("p ${OPTARG}") fi ;; - t) - TYPO3_VERSION=${OPTARG} - if ! [[ ${TYPO3_VERSION} =~ ^(11|12)$ ]]; then - INVALID_OPTIONS+=("p ${OPTARG}") - fi - ;; - e) - EXTRA_TEST_OPTIONS=${OPTARG} - ;; x) PHP_XDEBUG_ON=1 ;; y) PHP_XDEBUG_PORT=${OPTARG} ;; + n) + CGLCHECK_DRY_RUN=1 + ;; h) + loadHelp echo "${HELP}" exit 0 ;; - n) - CGLCHECK_DRY_RUN="-n" - ;; u) TEST_SUITE=update ;; - v) - SCRIPT_VERBOSE=1 - ;; \?) - INVALID_OPTIONS+=(${OPTARG}) + INVALID_OPTIONS+=("${OPTARG}") ;; :) - INVALID_OPTIONS+=(${OPTARG}) + INVALID_OPTIONS+=("${OPTARG}") ;; esac done @@ -247,149 +328,279 @@ if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then echo "-"${I} >&2 done echo >&2 - echo "${HELP}" >&2 + echo "call \".Build/Scripts/runTests.sh -h\" to display help and valid options" exit 1 fi -# Move "7.4" to "php74", the latter is the docker container name -DOCKER_PHP_IMAGE=`echo "php${PHP_VERSION}" | sed -e 's/\.//'` +handleDbmsOptions + +COMPOSER_ROOT_VERSION="13.0.x-dev" +HOST_UID=$(id -u) +USERSET="" +if [ $(uname) != "Darwin" ]; then + USERSET="--user $HOST_UID" +fi + +# Go to the directory this script is located, so everything else is relative +# to this dir, no matter from where this script is called, then go up two dirs. +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd "$THIS_SCRIPT_DIR" || exit 1 +cd ../../ || exit 1 +ROOT_DIR="${PWD}" + +# Create .cache dir: composer need this. +mkdir -p .Build/.cache +mkdir -p .Build/web/typo3temp/var/tests + +IMAGE_PREFIX="docker.io/" +# Non-CI fetches TYPO3 images (php and nodejs) from ghcr.io +TYPO3_IMAGE_PREFIX="ghcr.io/typo3/" +CONTAINER_INTERACTIVE="-it --init" + +IS_CORE_CI=0 +# ENV var "CI" is set by gitlab-ci. We use it here to distinct 'local' and 'CI' environment. +if [ "${CI}" == "true" ]; then + IS_CORE_CI=1 + IMAGE_PREFIX="" + CONTAINER_INTERACTIVE="" +fi + +# determine default container binary to use: 1. podman 2. docker +if [[ -z "${CONTAINER_BIN}" ]]; then + if type "podman" >/dev/null 2>&1; then + CONTAINER_BIN="podman" + elif type "docker" >/dev/null 2>&1; then + CONTAINER_BIN="docker" + fi +fi + +IMAGE_PHP="${TYPO3_IMAGE_PREFIX}core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest" +IMAGE_ALPINE="${IMAGE_PREFIX}alpine:3.8" +IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}" +IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}" +IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine" +IMAGE_DOCS="ghcr.io/typo3-documentation/render-guides:latest" # Set $1 to first mass argument, this is the optional test file or test directory to execute shift $((OPTIND - 1)) -TEST_FILE=${1} -if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x +SUFFIX=$(echo $RANDOM) +NETWORK="t3docsexamples-${SUFFIX}" +${CONTAINER_BIN} network create ${NETWORK} >/dev/null + +if [ ${CONTAINER_BIN} = "docker" ]; then + # docker needs the add-host for xdebug remote debugging. podman has host.container.internal built in + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" + CONTAINER_DOCS_PARAMS="${CONTAINER_INTERACTIVE} ${DOCS_PARAMS} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:/project" +else + # podman + CONTAINER_HOST="host.containers.internal" + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" + CONTAINER_DOCS_PARAMS="${CONTAINER_INTERACTIVE} ${DOCS_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:/project" +fi + +if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE="-e XDEBUG_MODE=off" + XDEBUG_CONFIG=" " +else + XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" + XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=${CONTAINER_HOST}" fi # Suite execution case ${TEST_SUITE} in - acceptance) - handleDbmsAndDriverOptions - setUpDockerComposeDotEnv - case ${DBMS} in - sqlite) - echo "Using driver: ${DATABASE_DRIVER}" - docker-compose run acceptance_cli_sqlite - SUITE_EXIT_CODE=$? - ;; - mysql) - echo "Using driver: ${DATABASE_DRIVER}" - docker-compose run acceptance_cli_mysql80 - SUITE_EXIT_CODE=$? - ;; - mariadb) - echo "Using driver: ${DATABASE_DRIVER}" - docker-compose run acceptance_cli_mariadb10 - SUITE_EXIT_CODE=$? - ;; - postgres) - docker-compose run acceptance_cli_postgres10 - SUITE_EXIT_CODE=$? - ;; - *) - echo "Acceptance tests don't run with DBMS ${DBMS}" >&2 - echo >&2 - echo "call \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 - exit 1 - esac - docker-compose down - ;; cgl) - # Active dry-run for cgl needs not "-n" but specific options - if [[ ! -z ${CGLCHECK_DRY_RUN} ]]; then - CGLCHECK_DRY_RUN="--dry-run --diff" + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND="php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v --dry-run --diff --config=Build/cgl/.php-cs-fixer.dist.php --using-cache=no ." + else + COMMAND="php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v --config=Build/cgl/.php-cs-fixer.dist.php --using-cache=no ." fi - setUpDockerComposeDotEnv - docker-compose run cgl + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; clean) - rm -rf ../../composer.lock ../../.Build/ ../../Tests/Acceptance/Support/_generated/ ../../composer.json.testing + cleanCacheFiles + cleanRenderedDocumentationFiles + ;; + cleanCache) + cleanCacheFiles + ;; + cleanRenderedDocumentation) + cleanRenderedDocumentationFiles + ;; + composer) + COMMAND=(composer "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + composerNormalize) + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND=(composer normalize -n) + else + COMMAND=(composer normalize) + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? ;; composerUpdate) - setUpDockerComposeDotEnv - cp ../../composer.json ../../composer.json.orig - if [ -f "../../composer.json.testing" ]; then - cp ../../composer.json ../../composer.json.orig + rm -rf .Build/bin/ .Build/typo3 .Build/vendor .Build/Web ./composer.lock + cp ${ROOT_DIR}/composer.json ${ROOT_DIR}/composer.json.orig + if [ -f "${ROOT_DIR}/composer.json.testing" ]; then + cp ${ROOT_DIR}/composer.json ${ROOT_DIR}/composer.json.orig fi - docker-compose run composer_update - cp ../../composer.json ../../composer.json.testing - mv ../../composer.json.orig ../../composer.json + COMMAND=(composer require --no-ansi --no-interaction --no-progress) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + cp ${ROOT_DIR}/composer.json ${ROOT_DIR}/composer.json.testing + mv ${ROOT_DIR}/composer.json.orig ${ROOT_DIR}/composer.json + ;; + composerUpdateRector) + rm -rf Build/rector/.Build/bin/ Build/rector/.Build/vendor Build/rector/composer.lock + cp ${ROOT_DIR}/Build/rector/composer.json ${ROOT_DIR}/Build/rector/composer.json.orig + if [ -f "${ROOT_DIR}/Build/rector/composer.json.testing" ]; then + cp ${ROOT_DIR}/Build/rector/composer.json ${ROOT_DIR}/Build/rector/composer.json.orig + fi + COMMAND=(composer require --working-dir=${ROOT_DIR}/Build/rector --no-ansi --no-interaction --no-progress) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + cp ${ROOT_DIR}/Build/rector/composer.json ${ROOT_DIR}/Build/rector/composer.json.testing + mv ${ROOT_DIR}/Build/rector/composer.json.orig ${ROOT_DIR}/Build/rector/composer.json + ;; + composerValidate) + COMMAND=(composer validate "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? - docker-compose down ;; functional) - handleDbmsAndDriverOptions - setUpDockerComposeDotEnv + CONTAINER_PARAMS="" + COMMAND=(.Build/bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} "$@") case ${DBMS} in mariadb) echo "Using driver: ${DATABASE_DRIVER}" - docker-compose run functional_mariadb10 + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; mysql) echo "Using driver: ${DATABASE_DRIVER}" - docker-compose run functional_mysql80 + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; postgres) - docker-compose run functional_postgres10 + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-func-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; sqlite) - # sqlite has a tmpfs as Web/typo3temp/var/tests/functional-sqlite-dbs/ - # Since docker is executed as root (yay!), the path to this dir is owned by - # root if docker creates it. Thank you, docker. We create the path beforehand - # to avoid permission issues. - mkdir -p ${ROOT_DIR}/Web/typo3temp/var/tests/functional-sqlite-dbs/ - docker-compose run functional_sqlite + # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues + mkdir -p "${ROOT_DIR}/.Build/web/typo3temp/var/tests/functional-sqlite-dbs/" + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${ROOT_DIR}/.Build/web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; - *) - echo "Invalid -d option argument ${DBMS}" >&2 - echo >&2 - echo "${HELP}" >&2 - exit 1 esac - docker-compose down ;; lint) - setUpDockerComposeDotEnv - docker-compose run lint + COMMAND="find . -name \\*.php ! -path "./.Build/\\*" -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; phpstan) - setUpDockerComposeDotEnv - docker-compose run phpstan + COMMAND="php -dxdebug.mode=off .Build/bin/phpstan --configuration=Build/phpstan/phpstan.neon" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + phpstanBaseline) + COMMAND="php -dxdebug.mode=off .Build/bin/phpstan --configuration=Build/phpstan/phpstan.neon --generate-baseline=Build/phpstan/phpstan-baseline.neon -v" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" SUITE_EXIT_CODE=$? - docker-compose down ;; - phpstanGenerateBaseline) - setUpDockerComposeDotEnv - docker-compose run phpstan_generate_baseline + rector) + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND=(php -dxdebug.mode=off Build/rector/.Build/bin/rector -n --config=Build/rector/rector.php --clear-cache "$@") + else + COMMAND=(php -dxdebug.mode=off Build/rector/.Build/bin/rector --config=Build/rector/rector.php --clear-cache "$@") + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name rector-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + renderDocumentation) + COMMAND=(--config=Documentation "$@") + mkdir -p Documentation-GENERATED-temp + ${CONTAINER_BIN} run ${CONTAINER_INTERACTIVE} ${CONTAINER_DOCS_PARAMS} --name render-documentation-${SUFFIX} ${IMAGE_DOCS} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + testRenderDocumentation) + COMMAND=(--config=Documentation --no-progress --fail-on-log "$@") + mkdir -p Documentation-GENERATED-temp + ${CONTAINER_BIN} run ${CONTAINER_INTERACTIVE} ${CONTAINER_DOCS_PARAMS} --name render-documentation-test-${SUFFIX} ${IMAGE_DOCS} "${COMMAND[@]}" SUITE_EXIT_CODE=$? - docker-compose down ;; unit) - setUpDockerComposeDotEnv - docker-compose run unit + COMMAND=(.Build/bin/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? - docker-compose down ;; update) - # pull typo3/core-testing-*:latest versions of those ones that exist locally - docker images typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} docker pull {} + # pull typo3/core-testing-* versions of those ones that exist locally + echo "> pull ${TYPO3_IMAGE_PREFIX}core-testing-* versions of those ones that exist locally" + ${CONTAINER_BIN} images "${TYPO3_IMAGE_PREFIX}core-testing-*" --format "{{.Repository}}:{{.Tag}}" | xargs -I {} ${CONTAINER_BIN} pull {} + echo "" # remove "dangling" typo3/core-testing-* images (those tagged as ) - docker images typo3/core-testing-* --filter "dangling=true" --format "{{.ID}}" | xargs -I {} docker rmi {} + echo "> remove \"dangling\" ${TYPO3_IMAGE_PREFIX}/core-testing-* images (those tagged as )" + ${CONTAINER_BIN} images --filter "reference=${TYPO3_IMAGE_PREFIX}/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi -f {} + echo "" ;; *) + loadHelp echo "Invalid -s option argument ${TEST_SUITE}" >&2 echo >&2 echo "${HELP}" >&2 exit 1 + ;; esac -exit $SUITE_EXIT_CODE +cleanUp + +# Print summary +echo "" >&2 +echo "###########################################################################" >&2 +echo "Result of ${TEST_SUITE}" >&2 +echo "Container runtime: ${CONTAINER_BIN}" >&2 +if [[ ${IS_CORE_CI} -eq 1 ]]; then + echo "Environment: CI" >&2 +else + echo "Environment: local" >&2 +fi +echo "PHP: ${PHP_VERSION}" >&2 +echo "TYPO3: ${CORE_VERSION}" >&2 +if [[ ${TEST_SUITE} =~ ^functional$ ]]; then + case "${DBMS}" in + mariadb|mysql) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 + ;; + postgres) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver pdo_pgsql" >&2 + ;; + sqlite) + echo "DBMS: ${DBMS} driver pdo_sqlite" >&2 + ;; + esac +fi +if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 +else + echo "FAILURE" >&2 +fi +echo "###########################################################################" >&2 +echo "" >&2 + +# Exit with code of test suite - This script return non-zero if the executed test failed. +exit $SUITE_EXIT_CODE \ No newline at end of file From 74a5100389771806ed8ad01bb89fc23d73a59ca7 Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 20:50:59 +0100 Subject: [PATCH 04/11] Update CGL class header --- Build/FunctionalTestsBootstrap.php | 2 +- Build/UnitTestsBootstrap.php | 2 +- Build/php-cs-fixer/config.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Build/FunctionalTestsBootstrap.php b/Build/FunctionalTestsBootstrap.php index 4040cac..8bad2c5 100644 --- a/Build/FunctionalTestsBootstrap.php +++ b/Build/FunctionalTestsBootstrap.php @@ -1,7 +1,7 @@ Date: Sat, 11 Jan 2025 20:52:49 +0100 Subject: [PATCH 05/11] Remove -t argument from runScript --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46cc7db..4b821c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,10 @@ jobs: uses: actions/checkout@v4 - name: Composer - run: Build/Scripts/runTests.sh -t 12 -p 8.1 -s composerUpdate + run: Build/Scripts/runTests.sh -p 8.1 -s composerUpdate - name: Lint PHP - run: Build/Scripts/runTests.sh -t 12 -p 8.1 -s lint + run: Build/Scripts/runTests.sh -p 8.1 -s lint - name: Validate code against CGL - run: Build/Scripts/runTests.sh -t 12 -p 8.1 -s cgl -n + run: Build/Scripts/runTests.sh -p 8.1 -s cgl -n From f7cf5b64c6d76f18394c030194df4ce40f5de7d4 Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 20:55:04 +0100 Subject: [PATCH 06/11] Use correct CGL path --- .../config.php => cgl/.php-cs-fixer.dist.php} | 0 Build/testing-docker/docker-compose.yml | 398 ------------------ 2 files changed, 398 deletions(-) rename Build/{php-cs-fixer/config.php => cgl/.php-cs-fixer.dist.php} (100%) delete mode 100644 Build/testing-docker/docker-compose.yml diff --git a/Build/php-cs-fixer/config.php b/Build/cgl/.php-cs-fixer.dist.php similarity index 100% rename from Build/php-cs-fixer/config.php rename to Build/cgl/.php-cs-fixer.dist.php diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml deleted file mode 100644 index 692b5ac..0000000 --- a/Build/testing-docker/docker-compose.yml +++ /dev/null @@ -1,398 +0,0 @@ -version: '2.3' -services: - mariadb10: - # not using mariadb:10 for the time being, because 10.5.7 (currently latest) is broken - image: mariadb:10.5.6 - environment: - MYSQL_ROOT_PASSWORD: funcp - tmpfs: - - /var/lib/mysql/:rw,noexec,nosuid - - mysql80: - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: funcp - tmpfs: - - /var/lib/mysql/:rw,noexec,nosuid - - postgres10: - image: postgres:10-alpine - environment: - POSTGRES_PASSWORD: funcp - POSTGRES_USER: ${HOST_USER} - tmpfs: - - /var/lib/postgresql/data:rw,noexec,nosuid - - acceptance_cli_mariadb10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - links: - - mariadb10 - environment: - typo3DatabaseDriver: "${DATABASE_DRIVER}" - typo3DatabaseName: func_test - typo3DatabaseUsername: root - typo3DatabasePassword: funcp - typo3DatabaseHost: mariadb10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR}/ - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mariadb10 3306; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - mkdir -p .Build/Web/typo3temp/var/tests/ - COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" $${COMMAND}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; - fi - " - - acceptance_cli_mysql80: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - links: - - mysql80 - environment: - typo3DatabaseDriver: "${DATABASE_DRIVER}" - typo3DatabaseName: func_test - typo3DatabaseUsername: root - typo3DatabasePassword: funcp - typo3DatabaseHost: mysql80 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mysql80 3306; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - mkdir -p .Build/Web/typo3temp/var/tests/ - COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" $${COMMAND}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; - fi - " - - acceptance_cli_postgres10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - links: - - postgres10 - environment: - typo3DatabaseDriver: pdo_pgsql - typo3DatabaseName: bamboo - typo3DatabaseUsername: ${HOST_USER} - typo3DatabaseHost: postgres10 - typo3DatabasePassword: funcp - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z postgres10 5432; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - mkdir -p .Build/Web/typo3temp/var/tests/ - COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" $${COMMAND}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; - fi - " - - acceptance_cli_sqlite: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - environment: - typo3DatabaseDriver: pdo_sqlite - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - mkdir -p Web/typo3temp/var/tests/ - COMMAND=\".Build/vendor/codeception/codeception/codecept run Cli -d -c Tests/codeception.yml ${TEST_FILE}\" - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" $${COMMAND}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" $${COMMAND}; - fi - " - - cgl: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --config=Build/php-cs-fixer/config.php - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" PHP_CS_FIXER_ALLOW_XDEBUG=1 .Build/bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --config=Build/php-cs-fixer/config.php - fi - " - - composer_update: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - environment: - COMPOSER_CACHE_DIR: ".cache/composer" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - if [ ${TYPO3_VERSION} -eq 11 ]; then - composer req --dev --no-update typo3/cms-composer-installers:^3.0 typo3/cms-workspaces:^11.5 typo3/cms-impexp:^11.5 - composer req typo3/cms-core:^11.5 --no-update - fi - if [ ${TYPO3_VERSION} -eq 12 ]; then - composer req --dev --no-update "typo3/cms-composer-installers:^5.0" typo3/cms-impexp:^12.4 typo3/cms-workspaces:^12.4 - composer req typo3/cms-core:^12.4 --no-update - fi - composer update --no-progress --no-interaction; - " - - functional_mariadb10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - links: - - mariadb10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - environment: - typo3DatabaseDriver: "${DATABASE_DRIVER}" - typo3DatabaseName: func_test - typo3DatabaseUsername: root - typo3DatabasePassword: funcp - typo3DatabaseHost: mariadb10 - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mariadb10 3306; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - php -dxdebug.mode=off .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - php -dxdebug.mode='debug,develop' XDEBUG_TRIGGER='foo' XDEBUG_CONFIG='client_host=host.docker.internal' .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " - - functional_mysql80: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - links: - - mysql80 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - environment: - typo3DatabaseDriver: "${DATABASE_DRIVER}" - typo3DatabaseName: func_test - typo3DatabaseUsername: root - typo3DatabasePassword: funcp - typo3DatabaseHost: mysql80 - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z mysql80 3306; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " - - functional_postgres10: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - links: - - postgres10 - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - environment: - typo3DatabaseDriver: pdo_pgsql - typo3DatabaseName: bamboo - typo3DatabaseUsername: ${HOST_USER} - typo3DatabaseHost: postgres10 - typo3DatabasePassword: funcp - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - echo Waiting for database start...; - while ! nc -z postgres10 5432; do - sleep 1; - done; - echo Database is up; - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}; - fi - " - - functional_sqlite: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - tmpfs: - - ${ROOT_DIR}/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid,uid=${HOST_UID} - environment: - typo3DatabaseDriver: pdo_sqlite - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}; - fi - " - - lint: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - find . -name \\*.php ! -path "./.Build/\\*" -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null - " - - phpstan: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - mkdir -p .Build/.cache - php -v | grep '^PHP'; - php -dxdebug.mode=off .Build/bin/phpstan analyze -c Build/phpstan.neon --no-progress - " - - phpstan_generate_baseline: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - mkdir -p .Build/.cache - php -v | grep '^PHP'; - php -dxdebug.mode=off .Build/bin/phpstan analyze -c Build/phpstan.neon --generate-baseline=Build/phpstan-baseline.neon - " - - unit: - image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest - user: "${HOST_UID}" - volumes: - - ${ROOT_DIR}:${ROOT_DIR} - working_dir: ${ROOT_DIR} - extra_hosts: - - "host.docker.internal:host-gateway" - command: > - /bin/sh -c " - if [ ${SCRIPT_VERBOSE} -eq 1 ]; then - set -x - fi - php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - XDEBUG_MODE=\"off\" .Build/bin/phpunit -c Build/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - else - XDEBUG_MODE=\"debug,develop\" XDEBUG_TRIGGER=\"foo\" XDEBUG_CONFIG=\"client_host=host.docker.internal\" .Build/bin/phpunit -c Build/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}; - fi - " \ No newline at end of file From c859014f4bea852c72c0057b30488210d76548f0 Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 20:58:51 +0100 Subject: [PATCH 07/11] Update .gitattributes --- .gitattributes | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index c38382e..779ec05 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,7 @@ +/.github/ export-ignore /Build/ export-ignore -/Resources/Private/Php/ export-ignore +/Tests/ export-ignore +/.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore -/.editorconfig export-ignore +/.crowdin.yml export-ignore \ No newline at end of file From f7925f206c489c4201c95a5df074f0cfc312be4b Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 21:00:07 +0100 Subject: [PATCH 08/11] Update .gitattributes --- .gitattributes | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 779ec05..153740a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,11 @@ /.github/ export-ignore +/.Build/ export-ignore /Build/ export-ignore /Tests/ export-ignore + /.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore -/.crowdin.yml export-ignore \ No newline at end of file +/.crowdin.yml export-ignore +/composer.json.testing export-ignore +/composer.lock export-ignore From 117b8cc2b1d546bdb3f2337034c07c33332dd10f Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 21:01:02 +0100 Subject: [PATCH 09/11] Update .gitignore --- .gitignore | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 42796e9..63300ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,48 @@ -# Git global ignore file -# for local exclude patterns please edit .git/info/exclude -# Example file see https://github.com/TYPO3-Documentation/T3DocTeam/blob/master/.gitignore +######################## +# plesk-widget +# global ignore file +######################## -# ignore generated documentation +# Ignore files generated by docs rendering *GENERATED* +docker-compose.yaml +docker-compose.yml -# ignore typical clutter of IDEs and editors (this could be added in .git/info/exclude, -# but we add it here for convenience) +# Ignore environment files +.env + +# Ignore temporary files (left by editors and OS) *~ *.bak +*.swp +.DS_Store + +# Ignore by common IDEs used directories/files +nbproject *.idea *.project -*.swp -.project +.buildpath +.settings +.TemporaryItems .webprj +.fleet + +# Temporary files and folders +/.cache +.php_cs.cache +.php-cs-fixer.cache +.sass-cache +.session +*.log + +# Ignore composer stuff +bin/* +vendor/* +.build +.php_cs.cache +composer.lock +# Ignore testing stuff +/.Build +/composer.json.orig +/composer.json.testing From 80d9980896a9bd73364ae4e7d68e7a0b093ce13f Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 21:01:14 +0100 Subject: [PATCH 10/11] Update .gitattributes --- .gitattributes | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitattributes b/.gitattributes index 153740a..779ec05 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,11 +1,7 @@ /.github/ export-ignore -/.Build/ export-ignore /Build/ export-ignore /Tests/ export-ignore - /.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore -/.crowdin.yml export-ignore -/composer.json.testing export-ignore -/composer.lock export-ignore +/.crowdin.yml export-ignore \ No newline at end of file From b49a2712c1dca7cfe6b404776df6b8abca717f45 Mon Sep 17 00:00:00 2001 From: Stefan Froemken Date: Sat, 11 Jan 2025 21:01:41 +0100 Subject: [PATCH 11/11] Update CGL of all PHP classes --- Classes/DataProvider/WebspaceDataProvider.php | 10 +-- Classes/Plesk/Site.php | 2 +- Classes/Service/PleskSiteService.php | 2 - Classes/Widget/PhpWidget.php | 1 - Classes/Widget/WebspaceWidget.php | 2 +- .../Php/plesk/api-php-lib/src/Api/Client.php | 67 +++++++++---------- .../api-php-lib/src/Api/Client/Exception.php | 12 ++-- .../plesk/api-php-lib/src/Api/Exception.php | 12 ++-- .../api-php-lib/src/Api/InternalClient.php | 10 ++- .../plesk/api-php-lib/src/Api/Operator.php | 17 +++-- .../api-php-lib/src/Api/Operator/Aps.php | 12 ++-- .../src/Api/Operator/Certificate.php | 8 ++- .../api-php-lib/src/Api/Operator/Customer.php | 8 ++- .../api-php-lib/src/Api/Operator/Database.php | 15 +++-- .../src/Api/Operator/DatabaseServer.php | 12 +++- .../api-php-lib/src/Api/Operator/Dns.php | 12 ++-- .../src/Api/Operator/DnsTemplate.php | 14 ++-- .../api-php-lib/src/Api/Operator/EventLog.php | 10 ++- .../plesk/api-php-lib/src/Api/Operator/Ip.php | 8 ++- .../api-php-lib/src/Api/Operator/Locale.php | 10 ++- .../src/Api/Operator/LogRotation.php | 12 ++-- .../api-php-lib/src/Api/Operator/Mail.php | 10 ++- .../src/Api/Operator/PhpHandler.php | 8 ++- .../src/Api/Operator/ProtectedDirectory.php | 12 ++-- .../api-php-lib/src/Api/Operator/Reseller.php | 10 ++- .../src/Api/Operator/ResellerPlan.php | 12 ++-- .../src/Api/Operator/SecretKey.php | 14 ++-- .../api-php-lib/src/Api/Operator/Server.php | 30 +++++---- .../src/Api/Operator/ServicePlan.php | 8 ++- .../src/Api/Operator/ServicePlanAddon.php | 12 ++-- .../api-php-lib/src/Api/Operator/Session.php | 12 +++- .../api-php-lib/src/Api/Operator/Site.php | 12 ++-- .../src/Api/Operator/SiteAlias.php | 11 +-- .../src/Api/Operator/Subdomain.php | 10 ++- .../plesk/api-php-lib/src/Api/Operator/Ui.php | 10 ++- .../src/Api/Operator/VirtualDirectory.php | 8 ++- .../api-php-lib/src/Api/Operator/Webspace.php | 12 ++-- .../Php/plesk/api-php-lib/src/Api/Struct.php | 23 ++++--- .../src/Api/Struct/Certificate/Info.php | 8 ++- .../src/Api/Struct/Customer/GeneralInfo.php | 8 ++- .../src/Api/Struct/Customer/Info.php | 8 ++- .../src/Api/Struct/Database/Info.php | 8 ++- .../src/Api/Struct/Database/UserInfo.php | 8 ++- .../src/Api/Struct/DatabaseServer/Info.php | 8 ++- .../api-php-lib/src/Api/Struct/Dns/Info.php | 8 ++- .../src/Api/Struct/EventLog/DetailedEvent.php | 8 ++- .../src/Api/Struct/EventLog/Event.php | 8 ++- .../api-php-lib/src/Api/Struct/Ip/Info.php | 8 ++- .../src/Api/Struct/Locale/Info.php | 8 ++- .../src/Api/Struct/Mail/GeneralInfo.php | 8 ++- .../api-php-lib/src/Api/Struct/Mail/Info.php | 8 ++- .../src/Api/Struct/PhpHandler/Info.php | 8 ++- .../Struct/ProtectedDirectory/DataInfo.php | 8 ++- .../Api/Struct/ProtectedDirectory/Info.php | 8 ++- .../Struct/ProtectedDirectory/UserInfo.php | 8 ++- .../src/Api/Struct/Reseller/GeneralInfo.php | 10 ++- .../src/Api/Struct/Reseller/Info.php | 8 ++- .../src/Api/Struct/SecretKey/Info.php | 8 ++- .../src/Api/Struct/Server/Admin.php | 8 ++- .../src/Api/Struct/Server/GeneralInfo.php | 8 ++- .../src/Api/Struct/Server/Preferences.php | 8 ++- .../Api/Struct/Server/SessionPreferences.php | 8 ++- .../src/Api/Struct/Server/Statistics.php | 10 ++- .../Struct/Server/Statistics/DiskSpace.php | 8 ++- .../Struct/Server/Statistics/LoadAverage.php | 8 ++- .../Api/Struct/Server/Statistics/Memory.php | 8 ++- .../Api/Struct/Server/Statistics/Objects.php | 8 ++- .../Api/Struct/Server/Statistics/Other.php | 8 ++- .../src/Api/Struct/Server/Statistics/Swap.php | 8 ++- .../Api/Struct/Server/Statistics/Version.php | 8 ++- .../src/Api/Struct/Server/UpdatesInfo.php | 8 ++- .../src/Api/Struct/ServicePlan/Info.php | 8 ++- .../src/Api/Struct/Session/Info.php | 8 ++- .../src/Api/Struct/Site/GeneralInfo.php | 8 ++- .../src/Api/Struct/Site/HostingInfo.php | 10 ++- .../api-php-lib/src/Api/Struct/Site/Info.php | 8 ++- .../src/Api/Struct/SiteAlias/GeneralInfo.php | 8 ++- .../src/Api/Struct/SiteAlias/Info.php | 8 ++- .../src/Api/Struct/Subdomain/Info.php | 10 ++- .../src/Api/Struct/Ui/CustomButton.php | 8 ++- .../src/Api/Struct/Webspace/DiskUsage.php | 9 ++- .../src/Api/Struct/Webspace/GeneralInfo.php | 10 ++- .../Struct/Webspace/HostingPropertyInfo.php | 8 ++- .../src/Api/Struct/Webspace/Info.php | 8 ++- .../src/Api/Struct/Webspace/Limit.php | 8 ++- .../Api/Struct/Webspace/LimitDescriptor.php | 10 ++- .../src/Api/Struct/Webspace/LimitInfo.php | 8 ++- .../src/Api/Struct/Webspace/Limits.php | 10 ++- .../Struct/Webspace/PermissionDescriptor.php | 10 ++- .../Api/Struct/Webspace/PermissionInfo.php | 8 ++- .../src/Api/Struct/Webspace/PhpSettings.php | 10 ++- .../Webspace/PhysicalHostingDescriptor.php | 10 ++- .../plesk/api-php-lib/src/Api/XmlResponse.php | 10 ++- ext_emconf.php | 7 ++ 94 files changed, 713 insertions(+), 227 deletions(-) diff --git a/Classes/DataProvider/WebspaceDataProvider.php b/Classes/DataProvider/WebspaceDataProvider.php index bf8fc6a..ce42961 100644 --- a/Classes/DataProvider/WebspaceDataProvider.php +++ b/Classes/DataProvider/WebspaceDataProvider.php @@ -39,15 +39,15 @@ public function getChartData(): array 0 => 'HTTP', 1 => 'Database', 2 => 'Logs', - 3 => 'Free' + 3 => 'Free', ], 'datasets' => [ [ 'backgroundColor' => WidgetApi::getDefaultChartColors(), 'border' => 0, - 'data' => $this->getWebSpaceStatus($pleskClient) - ] - ] + 'data' => $this->getWebSpaceStatus($pleskClient), + ], + ], ]; } @@ -72,7 +72,7 @@ private function getWebSpaceStatus(Client $pleskClient): array 3 => $this->calcDiskUsage( ($diskSpace - $diskUsage->httpdocs + $diskUsage->httpsdocs + $diskUsage->dbases + $diskUsage->logs), $diskSpace - ) + ), ]; } diff --git a/Classes/Plesk/Site.php b/Classes/Plesk/Site.php index f889615..b78cbe7 100644 --- a/Classes/Plesk/Site.php +++ b/Classes/Plesk/Site.php @@ -41,7 +41,7 @@ public function getHosting(): ?Hosting ], 'dataset' => [ 'hosting' => null, - ] + ], ], ], ]); diff --git a/Classes/Service/PleskSiteService.php b/Classes/Service/PleskSiteService.php index dc2f7f6..81cb057 100644 --- a/Classes/Service/PleskSiteService.php +++ b/Classes/Service/PleskSiteService.php @@ -12,7 +12,6 @@ namespace StefanFroemken\PleskWidget\Service; use PleskX\Api\Client; -use PleskX\Api\Struct\Site\GeneralInfo; use StefanFroemken\PleskWidget\Plesk\Site; use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; @@ -21,7 +20,6 @@ class PleskSiteService public function __construct(private readonly FrontendInterface $cache) {} /** - * @param Client $pleskClient * @return Site[] */ public function getSites(Client $pleskClient): array diff --git a/Classes/Widget/PhpWidget.php b/Classes/Widget/PhpWidget.php index 42a98f8..f2a7da2 100644 --- a/Classes/Widget/PhpWidget.php +++ b/Classes/Widget/PhpWidget.php @@ -15,7 +15,6 @@ use StefanFroemken\PleskWidget\Client\ExtensionSettingException; use StefanFroemken\PleskWidget\Client\PleskClientFactory; use StefanFroemken\PleskWidget\Configuration\ExtConf; -use StefanFroemken\PleskWidget\DataProvider\PhpDataProvider; use StefanFroemken\PleskWidget\Service\PleskSiteService; use TYPO3\CMS\Backend\View\BackendViewFactory; use TYPO3\CMS\Dashboard\Widgets\RequestAwareWidgetInterface; diff --git a/Classes/Widget/WebspaceWidget.php b/Classes/Widget/WebspaceWidget.php index 05f40b7..1c47fe8 100644 --- a/Classes/Widget/WebspaceWidget.php +++ b/Classes/Widget/WebspaceWidget.php @@ -63,7 +63,7 @@ public function getEventData(): array ], 'title' => [ 'display' => true, - 'text' => 'Usage in ' . $this->extConf->getDiskUsageType() + 'text' => 'Usage in ' . $this->extConf->getDiskUsageType(), ], 'tooltip' => [ 'enabled' => true, diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php index 80e83be..7fb24d9 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php @@ -1,5 +1,11 @@ _version; $content = ""; - $content .= ''; + $content .= ''; return new SimpleXMLElement($content); } @@ -163,9 +167,9 @@ public function request($request, $mode = self::RESPONSE_SHORT) } } - if ('sdk' == $this->_protocol) { - $version = ('' == $this->_version) ? null : $this->_version; - $requestXml = new SimpleXMLElement((string) $request); + if ($this->_protocol == 'sdk') { + $version = ($this->_version == '') ? null : $this->_version; + $requestXml = new SimpleXMLElement((string)$request); $xml = \pm_ApiRpc::getService($version)->call($requestXml->children()[0]->asXml(), $this->_login); } else { $xml = $this->_performHttpRequest($request); @@ -175,7 +179,7 @@ public function request($request, $mode = self::RESPONSE_SHORT) ? call_user_func($this->_verifyResponseCallback, $xml) : $this->_verifyResponse($xml); - return (self::RESPONSE_FULL == $mode) ? $xml : $xml->xpath('//result')[0]; + return ($mode == self::RESPONSE_FULL) ? $xml : $xml->xpath('//result')[0]; } /** @@ -199,13 +203,13 @@ private function _performHttpRequest($request) curl_setopt($curl, CURLOPT_HTTPHEADER, $this->_getHeaders()); curl_setopt($curl, CURLOPT_POSTFIELDS, $request); - if ('' !== $this->_proxy) { + if ($this->_proxy !== '') { curl_setopt($curl, CURLOPT_PROXY, $this->_proxy); } $result = curl_exec($curl); - if (false === $result) { + if ($result === false) { throw new Client\Exception(curl_error($curl), curl_errno($curl)); } @@ -219,7 +223,6 @@ private function _performHttpRequest($request) /** * Perform multiple API requests using single HTTP request. * - * @param $requests * @param int $mode * * @throws Client\Exception @@ -233,21 +236,20 @@ public function multiRequest($requests, $mode = self::RESPONSE_SHORT) foreach ($requests as $request) { if ($request instanceof SimpleXMLElement) { throw new Client\Exception('SimpleXML type of request is not supported for multi requests.'); - } else { - if (is_array($request)) { - $request = $this->_arrayToXml($request, $requestXml)->asXML(); - } elseif (preg_match('/^[a-z]/', $request)) { - $this->_expandRequestShortSyntax($request, $requestXml); - } } + if (is_array($request)) { + $request = $this->_arrayToXml($request, $requestXml)->asXML(); + } elseif (preg_match('/^[a-z]/', $request)) { + $this->_expandRequestShortSyntax($request, $requestXml); + } + $responses[] = $this->request($request); } - if ('sdk' == $this->_protocol) { + if ($this->_protocol == 'sdk') { throw new Client\Exception('Multi requests are not supported via SDK.'); - } else { - $responseXml = $this->_performHttpRequest($requestXml->asXML()); } + $responseXml = $this->_performHttpRequest($requestXml->asXML()); $responses = []; foreach ($responseXml->children() as $childNode) { @@ -259,7 +261,7 @@ public function multiRequest($requests, $mode = self::RESPONSE_SHORT) $dom->documentElement->appendChild($childDomNode); $response = simplexml_load_string($dom->saveXML()); - $responses[] = (self::RESPONSE_FULL == $mode) ? $response : $response->xpath('//result')[0]; + $responses[] = ($mode == self::RESPONSE_FULL) ? $response : $response->xpath('//result')[0]; } return $responses; @@ -296,13 +298,13 @@ protected function _getHeaders() */ protected function _verifyResponse($xml) { - if ($xml->system && $xml->system->status && 'error' == (string) $xml->system->status) { - throw new Exception((string) $xml->system->errtext, (int) $xml->system->errcode); + if ($xml->system && $xml->system->status && (string)$xml->system->status == 'error') { + throw new Exception((string)$xml->system->errtext, (int)$xml->system->errcode); } if ($xml->xpath('//status[text()="error"]') && $xml->xpath('//errcode') && $xml->xpath('//errtext')) { - $errorCode = (int) $xml->xpath('//errcode')[0]; - $errorMessage = (string) $xml->xpath('//errtext')[0]; + $errorCode = (int)$xml->xpath('//errcode')[0]; + $errorMessage = (string)$xml->xpath('//errtext')[0]; throw new Exception($errorMessage, $errorCode); } @@ -312,7 +314,6 @@ protected function _verifyResponse($xml) * Expand short syntax (some.method.call) into full XML representation. * * @param string $request - * @param SimpleXMLElement $xml * * @return string */ @@ -327,7 +328,7 @@ protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml) if ($part !== $lastParts) { $node = $node->addChild($name); } else { - $node->{$name} = (string) $value; + $node->{$name} = (string)$value; } } @@ -337,8 +338,6 @@ protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml) /** * Convert array to XML representation. * - * @param array $array - * @param SimpleXMLElement $xml * @param string $parentEl * * @return SimpleXMLElement @@ -350,7 +349,7 @@ protected function _arrayToXml(array $array, SimpleXMLElement $xml, $parentEl = if (is_array($value)) { $this->_arrayToXml($value, $this->_isAssocArray($value) ? $xml->addChild($el) : $xml, $el); } else { - $xml->{$el} = (string) $value; + $xml->{$el} = (string)$value; } } @@ -358,8 +357,6 @@ protected function _arrayToXml(array $array, SimpleXMLElement $xml, $parentEl = } /** - * @param array $array - * * @return bool */ protected function _isAssocArray(array $array) @@ -375,7 +372,7 @@ protected function _isAssocArray(array $array) protected function _getOperator($name) { if (!isset($this->_operatorsCache[$name])) { - $className = '\\PleskX\\Api\\Operator\\'.$name; + $className = '\\PleskX\\Api\\Operator\\' . $name; $this->_operatorsCache[$name] = new $className($this); } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php index 0d26d30..66860c5 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php @@ -1,11 +1,15 @@ _client = $client; if (is_null($this->_wrapperTag)) { - $classNameParts = explode('\\', get_class($this)); + $classNameParts = explode('\\', static::class); $this->_wrapperTag = end($classNameParts); $this->_wrapperTag = strtolower(preg_replace('/([a-z])([A-Z])/', '\1-\2', $this->_wrapperTag)); } @@ -62,7 +68,7 @@ protected function _delete($field, $value, $deleteMethodName = 'del') ], ]); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } /** @@ -70,9 +76,6 @@ protected function _delete($field, $value, $deleteMethodName = 'del') * @param string $infoTag * @param string|null $field * @param int|string|null $value - * @param callable|null $filter - * - * @return mixed */ protected function _getItems($structClass, $infoTag, $field = null, $value = null, callable $filter = null) { diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php index 9f4bde3..54f9600 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php @@ -1,8 +1,12 @@ addChild($this->_wrapperTag)->addChild($command); foreach ($properties as $name => $value) { - if (false !== strpos($value, '&')) { + if (str_contains($value, '&')) { $info->$name = $value; continue; } @@ -50,15 +55,13 @@ private function _process($command, array $properties) } /** - * @param array $properties - * * @return bool */ public function updateUser(array $properties) { $response = $this->_process('set-db-user', $properties); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php index fc338ea..751967b 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php @@ -1,5 +1,11 @@ request('get-supported-types'); - return (array) $response->type; + return (array)$response->type; } /** @@ -61,7 +67,7 @@ private function _get($field = null, $value = null) $items = []; foreach ($response->xpath('//result') as $xmlResult) { $item = new Struct\Info($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php index 30920a7..91ef5ec 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php @@ -1,5 +1,11 @@ xpath('//result') as $xmlResult) { $item = new Struct\Info($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } @@ -106,7 +111,6 @@ public function delete($field, $value) /** * Delete multiply records by one request. * - * @param array $recordIds * * @return \PleskX\Api\XmlResponse[] */ diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php index 18e6634..40391af 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php @@ -1,5 +1,11 @@ xpath('//result') as $xmlResult) { $item = new Struct\Info($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } @@ -83,6 +87,6 @@ public function delete($field, $value) $response = $this->_client->request($packet); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php index 6b7325a..210f442 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php @@ -1,5 +1,11 @@ request('get-last-id')->getValue('id'); + return (int)$this->request('get-last-id')->getValue('id'); } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php index 4bc87f7..91550c0 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php @@ -1,5 +1,11 @@ _client->request($packet, \PleskX\Api\Client::RESPONSE_FULL); foreach ($response->locale->get->result as $localeInfo) { - $locales[(string) $localeInfo->info->id] = new Struct\Info($localeInfo->info); + $locales[(string)$localeInfo->info->id] = new Struct\Info($localeInfo->info); } return !is_null($id) ? reset($locales) : $locales; diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php index ef0b60e..e745205 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php @@ -1,8 +1,12 @@ {$field} = $value; $response = $this->_client->request($packet); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php index 66f1ec1..7ab15ed 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php @@ -1,5 +1,11 @@ xpath('//result') as $xmlResult) { $item = new Struct\GeneralInfo($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php index 17d533f..b79f591 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php @@ -1,8 +1,12 @@ _client->getPacket(); $createTag = $packet->addChild($this->_wrapperTag)->addChild('create'); - if ('' !== $ipAddress) { + if ($ipAddress !== '') { $createTag->addChild('ip_address', $ipAddress); } - if ('' !== $description) { + if ($description !== '') { $createTag->addChild('description', $description); } $response = $this->_client->request($packet); - return (string) $response->key; + return (string)$response->key; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php index 65cbae6..420a951 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php @@ -1,5 +1,11 @@ addChild($this->_wrapperTag)->addChild('get_protos'); $response = $this->_client->request($packet); - return (array) $response->protos->proto; + return (array)$response->protos->proto; } public function getGeneralInfo() @@ -43,7 +49,7 @@ public function getKeyInfo() $keyInfoXml = $this->_getInfo('key'); foreach ($keyInfoXml->property as $property) { - $keyInfo[(string) $property->name] = (string) $property->value; + $keyInfo[(string)$property->name] = (string)$property->value; } return $keyInfo; @@ -58,7 +64,7 @@ public function getComponents() $componentsXml = $this->_getInfo('components'); foreach ($componentsXml->component as $component) { - $components[(string) $component->name] = (string) $component->version; + $components[(string)$component->name] = (string)$component->version; } return $components; @@ -73,10 +79,10 @@ public function getServiceStates() $statesXml = $this->_getInfo('services_state'); foreach ($statesXml->srv as $service) { - $states[(string) $service->id] = [ - 'id' => (string) $service->id, - 'title' => (string) $service->title, - 'state' => (string) $service->state, + $states[(string)$service->id] = [ + 'id' => (string)$service->id, + 'title' => (string)$service->title, + 'state' => (string)$service->state, ]; } @@ -97,7 +103,7 @@ public function getShells() $shellsXml = $this->_getInfo('shells'); foreach ($shellsXml->shell as $shell) { - $shells[(string) $shell->name] = (string) $shell->path; + $shells[(string)$shell->name] = (string)$shell->path; } return $shells; @@ -110,7 +116,7 @@ public function getNetworkInterfaces() { $interfacesXml = $this->_getInfo('interfaces'); - return (array) $interfacesXml->interface; + return (array)$interfacesXml->interface; } public function getStatistics() @@ -127,7 +133,7 @@ public function getSiteIsolationConfig() $configXml = $this->_getInfo('site-isolation-config'); foreach ($configXml->property as $property) { - $config[(string) $property->name] = (string) $property->value; + $config[(string)$property->name] = (string)$property->value; } return $config; @@ -154,7 +160,7 @@ public function createSession($login, $clientIp) $dataNode->addChild('source_server'); $response = $this->_client->request($packet); - return (string) $response->id; + return (string)$response->id; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php index 41fa075..e440693 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php @@ -1,5 +1,11 @@ request('get'); foreach ($response->session as $sessionInfo) { - $sessions[(string) $sessionInfo->id] = new Struct\Info($sessionInfo); + $sessions[(string)$sessionInfo->id] = new Struct\Info($sessionInfo); } return $sessions; @@ -31,6 +37,6 @@ public function terminate($sessionId) { $response = $this->request("terminate.session-id=$sessionId"); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php index f92eae6..8949793 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php @@ -1,5 +1,11 @@ data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php index 187fcd2..01bab45 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php @@ -1,5 +1,11 @@ _client->request($packet); - return (int) $response->id; + return (int)$response->id; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php index 1c2ae8b..79ed025 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php @@ -1,5 +1,11 @@ getDocComment(); $propertyType = preg_replace('/^.+ @var ([a-z]+) .+$/', '\1', $docBlock); - if ('string' == $propertyType) { - $value = (string) $value; - } elseif ('int' == $propertyType) { - $value = (int) $value; - } elseif ('bool' == $propertyType) { - $value = in_array((string) $value, ['true', 'on', 'enabled']); + if ($propertyType == 'string') { + $value = (string)$value; + } elseif ($propertyType == 'int') { + $value = (int)$value; + } elseif ($propertyType == 'bool') { + $value = in_array((string)$value, ['true', 'on', 'enabled']); } else { throw new \Exception("Unknown property type '$propertyType'."); } @@ -56,7 +61,7 @@ protected function _initScalarProperties($apiResponse, array $properties) */ private function _underToCamel($under) { - $under = '_'.str_replace('_', ' ', strtolower($under)); + $under = '_' . str_replace('_', ' ', strtolower($under)); return ltrim(str_replace(' ', '', ucwords($under)), '_'); } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php index 7689bcb..129ac11 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php @@ -1,5 +1,11 @@ permissions = []; foreach ($apiResponse->permissions->permission as $permissionInfo) { - $this->permissions[(string) $permissionInfo->name] = (string) $permissionInfo->value; + $this->permissions[(string)$permissionInfo->name] = (string)$permissionInfo->value; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php index a1b6bf8..2df7d10 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php @@ -1,5 +1,11 @@ diskSpace = []; foreach ($apiResponse->diskspace as $disk) { - $this->diskSpace[(string) $disk->device->name] = new Statistics\DiskSpace($disk->device); + $this->diskSpace[(string)$disk->device->name] = new Statistics\DiskSpace($disk->device); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php index 1f21655..ae964b8 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php @@ -1,5 +1,11 @@ vrt_hst->property as $property) { - $this->properties[(string) $property->name] = (string) $property->value; + $this->properties[(string)$property->name] = (string)$property->value; } $this->_initScalarProperties($apiResponse->vrt_hst, [ 'ip_address', diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php index ea69dd9..df17f1f 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php @@ -1,5 +1,11 @@ property as $propertyInfo) { - $this->properties[(string) $propertyInfo->name] = (string) $propertyInfo->value; + $this->properties[(string)$propertyInfo->name] = (string)$propertyInfo->value; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php index ed7a712..417951a 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php @@ -1,5 +1,11 @@ dns_ip_address as $ip) { - $this->ipAddresses[] = (string) $ip; + $this->ipAddresses[] = (string)$ip; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php index 7922d18..d5024a9 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php @@ -1,5 +1,11 @@ limits = []; foreach ($apiResponse->descriptor->property as $propertyInfo) { - $this->limits[(string) $propertyInfo->name] = new LimitInfo($propertyInfo); + $this->limits[(string)$propertyInfo->name] = new LimitInfo($propertyInfo); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php index b8af3b6..2cb0614 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php @@ -1,5 +1,11 @@ limits = []; foreach ($apiResponse->limit as $limit) { - $this->limits[(string) $limit->name] = new Limit($limit); + $this->limits[(string)$limit->name] = new Limit($limit); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php index bc333ba..106611a 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php @@ -1,5 +1,11 @@ permissions = []; foreach ($apiResponse->descriptor->property as $propertyInfo) { - $this->permissions[(string) $propertyInfo->name] = new PermissionInfo($propertyInfo); + $this->permissions[(string)$propertyInfo->name] = new PermissionInfo($propertyInfo); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php index 9414a31..34b66b5 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php @@ -1,5 +1,11 @@ properties = []; foreach ($apiResponse->webspace->get->result->data->{'php-settings'}->setting as $setting) { - $this->properties[(string) $setting->name] = (string) $setting->value; + $this->properties[(string)$setting->name] = (string)$setting->value; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php index 1aff2f7..e9b0af9 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php @@ -1,5 +1,11 @@ properties = []; foreach ($apiResponse->descriptor->property as $propertyInfo) { - $this->properties[(string) $propertyInfo->name] = new HostingPropertyInfo($propertyInfo); + $this->properties[(string)$propertyInfo->name] = new HostingPropertyInfo($propertyInfo); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php index 687b6d8..34f7560 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php @@ -1,5 +1,11 @@ xpath('//'.$node)[0]; + return (string)$this->xpath('//' . $node)[0]; } } diff --git a/ext_emconf.php b/ext_emconf.php index e85fedf..d1a3757 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,5 +1,12 @@ 'Plesk Widget', 'description' => 'Shows information about your plesk based customer control center',