diff --git a/.github/workflows/testcore13.yml b/.github/workflows/testcore13.yml index a51805e..e036704 100644 --- a/.github/workflows/testcore13.yml +++ b/.github/workflows/testcore13.yml @@ -40,8 +40,8 @@ jobs: - name: "Find duplicate exception codes" run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s checkExceptionCodes" -# - name: "Run PHPStan" -# run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s phpstan" + - name: "Run PHPStan" + run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s phpstan" # testsuite: # name: all tests with core v13 diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 5594390..f2a961c 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -218,14 +218,14 @@ Options: -t <13> Only with -s composerInstall|composerInstallMin|composerInstallMax Specifies the TYPO3 CORE Version to be used - - 12: (default) use TYPO3 v13 + - 13: (default) use TYPO3 v13 - -p <8.1|8.2|8.3|8.4|8.5> + -p <8.2|8.3|8.4|8.5> Specifies the PHP minor version to be used - 8.2: (default) use PHP 8.2 - 8.3: use PHP 8.3 - 8.4: use PHP 8.4 - - 8.4: use PHP 8.5 + - 8.5: use PHP 8.5 -x Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom|acceptance|acceptanceInstall Send information to host instance for test or system under test break points. This is especially @@ -255,22 +255,22 @@ Options: Show this help. Examples: - # Run all core unit tests using PHP 8.1 + # Run all core unit tests using PHP 8.2 ./Build/Scripts/runTests.sh ./Build/Scripts/runTests.sh -s unit # Run all core units tests and enable xdebug (have a PhpStorm listening on port 9003!) ./Build/Scripts/runTests.sh -x - # Run unit tests in phpunit verbose mode with xdebug on PHP 8.1 and filter for test canRetrieveValueWithGP - ./Build/Scripts/runTests.sh -x -p 8.1 -e "-v --filter canRetrieveValueWithGP" + # Run unit tests in phpunit verbose mode with xdebug on PHP 8.2 and filter for test canRetrieveValueWithGP + ./Build/Scripts/runTests.sh -x -p 8.2 -e "-v --filter canRetrieveValueWithGP" # Run functional tests in phpunit with a filtered test method name in a specified file # example will currently execute two tests, both of which start with the search term ./Build/Scripts/runTests.sh -s functional -e "--filter deleteContent" typo3/sysext/core/Tests/Functional/DataHandling/Regular/Modify/ActionTest.php - # Run functional tests on postgres with xdebug, php 8.1 and execute a restricted set of tests - ./Build/Scripts/runTests.sh -x -p 8.1 -s functional -d postgres typo3/sysext/core/Tests/Functional/Authentication + # Run functional tests on postgres with xdebug, php 8.2 and execute a restricted set of tests + ./Build/Scripts/runTests.sh -x -p 8.2 -s functional -d postgres typo3/sysext/core/Tests/Functional/Authentication # Run functional tests on postgres 11 ./Build/Scripts/runTests.sh -s functional -d postgres -k 11 diff --git a/Build/phpstan/Core13/phpstan.neon b/Build/phpstan/Core13/phpstan.neon index f85bac5..20cf196 100644 --- a/Build/phpstan/Core13/phpstan.neon +++ b/Build/phpstan/Core13/phpstan.neon @@ -10,6 +10,7 @@ parameters: paths: - ../../../Classes/ + - ../../../Core13/ - ../../../Tests/ excludePaths: diff --git a/Classes/Client/ClientFactory.php b/Classes/Client/ClientFactory.php index d0f7d4e..c5be134 100644 --- a/Classes/Client/ClientFactory.php +++ b/Classes/Client/ClientFactory.php @@ -36,6 +36,9 @@ public function __construct( ) { } + /** + * @param array $options + */ public function buildDeeplClient(array $options = []): DeepLClient { if ($this->configuration->getApiKey() === '') { diff --git a/Classes/Controller/CkEditorController.php b/Classes/Controller/CkEditorController.php index eb3f590..b077694 100644 --- a/Classes/Controller/CkEditorController.php +++ b/Classes/Controller/CkEditorController.php @@ -50,13 +50,17 @@ public function deeplConfiguredAction(ServerRequestInterface $request): Response public function optimizeTextAction(ServerRequestInterface $request): ResponseInterface { $data = $request->getParsedBody(); - $splittedText = $this->htmlParser->splitHtml($data['text']); - foreach ($splittedText as $node => $text) { + $data = is_array($data) ? $data : []; + $text = (string)($data['text'] ?? ''); + $style = (string)($data['style'] ?? ''); + $tone = (string)($data['tone'] ?? ''); + $splittedText = $this->htmlParser->splitHtml($text); + foreach ($splittedText as $node => $value) { $optimizedText = $this->deeplService->rephraseText( - $data['text'], + $text, null, - RephraseWritingStyleDeepL::tryFrom($data['style']), - RephraseToneDeepL::tryFrom($data['tone']) + RephraseWritingStyleDeepL::tryFrom($style), + RephraseToneDeepL::tryFrom($tone) ); $splittedText[$node] = $optimizedText; } diff --git a/Classes/Domain/Enum/RephraseSupportedDeepLLanguage.php b/Classes/Domain/Enum/RephraseSupportedDeepLLanguage.php index b779886..4bb09f3 100644 --- a/Classes/Domain/Enum/RephraseSupportedDeepLLanguage.php +++ b/Classes/Domain/Enum/RephraseSupportedDeepLLanguage.php @@ -46,6 +46,9 @@ final class RephraseSupportedDeepLLanguage ], ]; + /** + * @return list + */ public static function getAllLanguages(): array { return array_keys(self::LANGUAGES); @@ -66,7 +69,7 @@ public static function isWritingStyleSupported(string $language): bool if (!array_key_exists($language, self::LANGUAGES)) { return false; } - return self::LANGUAGES[$language]['writing_style'] ?? false; + return self::LANGUAGES[$language]['writing_style']; } public static function isToneSupportedByLanguage(string $language): bool @@ -74,6 +77,6 @@ public static function isToneSupportedByLanguage(string $language): bool if (!array_key_exists($language, self::LANGUAGES)) { return false; } - return self::LANGUAGES[$language]['tone'] ?? false; + return self::LANGUAGES[$language]['tone']; } } diff --git a/Classes/FieldType/AbstractFieldType.php b/Classes/FieldType/AbstractFieldType.php index 68a6bfb..e5361af 100644 --- a/Classes/FieldType/AbstractFieldType.php +++ b/Classes/FieldType/AbstractFieldType.php @@ -6,6 +6,9 @@ abstract class AbstractFieldType implements FieldTypeInterface { + /** + * @param array $configuration + */ final public function __construct( protected readonly array $configuration, protected readonly string $table, diff --git a/Classes/FieldType/FieldTypeInterface.php b/Classes/FieldType/FieldTypeInterface.php index a6485db..f303400 100644 --- a/Classes/FieldType/FieldTypeInterface.php +++ b/Classes/FieldType/FieldTypeInterface.php @@ -6,6 +6,9 @@ interface FieldTypeInterface { + /** + * @param array $configuration + */ public function __construct( array $configuration, string $table, diff --git a/Classes/FieldType/FieldTypeRegistry.php b/Classes/FieldType/FieldTypeRegistry.php index 048d34e..3e90ad0 100644 --- a/Classes/FieldType/FieldTypeRegistry.php +++ b/Classes/FieldType/FieldTypeRegistry.php @@ -7,20 +7,23 @@ final class FieldTypeRegistry { /** - * @var array + * @var array> */ private static array $fieldTypes = [ 'input' => Input::class, 'text' => Text::class, ]; + /** + * @param array $tcaConfig + */ public static function getFieldProcessingTypeByRenderType( array $tcaConfig, string $table, string $fieldName, ?string $type = null ): FieldTypeInterface { - $renderType = $tcaConfig['renderType'] ?? $tcaConfig['type']; + $renderType = (string)($tcaConfig['renderType'] ?? $tcaConfig['type'] ?? ''); return new self::$fieldTypes[$renderType]( $tcaConfig, $table, diff --git a/Classes/FieldType/Input.php b/Classes/FieldType/Input.php index a6d6075..2fcfbfc 100644 --- a/Classes/FieldType/Input.php +++ b/Classes/FieldType/Input.php @@ -9,7 +9,7 @@ final class Input extends AbstractFieldType public function getTextForProcessing( string|int $value ): array { - return [$value]; + return [(string)$value]; } public function getValueForDatabase(array $processedText): int|string diff --git a/Classes/FieldType/Text.php b/Classes/FieldType/Text.php index f476389..e085646 100644 --- a/Classes/FieldType/Text.php +++ b/Classes/FieldType/Text.php @@ -14,6 +14,7 @@ final class Text extends AbstractFieldType public function getTextForProcessing( string|int $value ): array { + $value = (string)$value; if ($this->isRteField()) { $processing = $this->getHtmlParser()->splitHtml($value); } else { diff --git a/Classes/Form/UserFunc/WriteSupport.php b/Classes/Form/UserFunc/WriteSupport.php index 595ba40..44058a4 100644 --- a/Classes/Form/UserFunc/WriteSupport.php +++ b/Classes/Form/UserFunc/WriteSupport.php @@ -11,6 +11,9 @@ final class WriteSupport { + /** + * @param array $configuration + */ public function getSupportedLanguageForField(array &$configuration): void { foreach (RephraseSupportedDeepLLanguage::getAllLanguages() as $supportedLanguage) { @@ -21,6 +24,9 @@ public function getSupportedLanguageForField(array &$configuration): void } } + /** + * @param array $configuration + */ public function getSupportedToneForField(array &$configuration): void { foreach (RephraseToneDeepL::cases() as $supportedTone) { @@ -31,6 +37,9 @@ public function getSupportedToneForField(array &$configuration): void } } + /** + * @param array $configuration + */ public function getSupportedWritingStyleForField(array &$configuration): void { foreach (RephraseWritingStyleDeepL::cases() as $supportedWritingStyle) { @@ -42,7 +51,7 @@ public function getSupportedWritingStyleForField(array &$configuration): void } /** - * @param array{record?: array{deeplTargetLanguage?: array|string|null}} $params + * @param array{record?: array{deeplWriteLanguage?: array|string|null}} $params */ public function languageIsRephraseSupported(array $params, EvaluateDisplayConditions $conditions): bool { diff --git a/Classes/Hooks/WriteHook.php b/Classes/Hooks/WriteHook.php index 03d9fdb..84af4b5 100644 --- a/Classes/Hooks/WriteHook.php +++ b/Classes/Hooks/WriteHook.php @@ -43,14 +43,17 @@ public function processCmdmap( return; } - $recordId = $dataHandler->localize($table, $id, $value); + $recordId = $dataHandler->localize($table, (int)$id, $value); // localization went wrong if ($recordId === false) { return; } - $originalRecord = BackendUtility::getRecord($table, $id); - $translatedRecord = BackendUtility::getRecordLocalization($table, $id, $value); + $originalRecord = BackendUtility::getRecord($table, (int)$id); + if ($originalRecord === null) { + return; + } + $translatedRecord = BackendUtility::getRecordLocalization($table, (int)$id, $value); if ($translatedRecord === null) { return; @@ -66,6 +69,10 @@ public function processCmdmap( $commandIsProcessed = true; } + /** + * @param array $translatedRecord + * @param array $originalRecord + */ private function processRecordFieldsAndUpdate(string $table, array $translatedRecord, array $originalRecord, int|string $languageId): void { $pid = ($table === 'pages') ? $originalRecord['uid'] : $originalRecord['pid']; diff --git a/Classes/Service/DeeplService.php b/Classes/Service/DeeplService.php index b551ca5..3f32719 100644 --- a/Classes/Service/DeeplService.php +++ b/Classes/Service/DeeplService.php @@ -22,7 +22,7 @@ final class DeeplService { private const SENTENCE_SPLIT = '/([.?!]\s)/'; - private ?DeepLClient $deeplClient; + private DeepLClient $deeplClient; public function __construct( ClientFactory $clientFactory, @@ -76,10 +76,7 @@ public function rephraseText( } /** - * @param array{ - * writing_style?: string, - * tone?: string - * }|empty $options + * @param array $options * @throws DeepLException */ private function optimizeText( @@ -92,6 +89,9 @@ private function optimizeText( $targetLanguage, $options ); + if (is_array($rephrased)) { + $rephrased = $rephrased[0]; + } return (string)$rephrased; } @@ -107,6 +107,9 @@ private function splitTextToMaxSize(string $text): array } $sentences = preg_split(self::SENTENCE_SPLIT, $text, -1, PREG_SPLIT_DELIM_CAPTURE); + if ($sentences === false) { + return [$text]; + } $countResult = count($sentences); $snippets = []; diff --git a/Classes/Service/HtmlParser.php b/Classes/Service/HtmlParser.php index bd1b260..a55b82e 100644 --- a/Classes/Service/HtmlParser.php +++ b/Classes/Service/HtmlParser.php @@ -17,6 +17,9 @@ */ final class HtmlParser { + /** + * @return array + */ public function splitHtml(string $value): array { // The template tag is necessary! @@ -58,6 +61,9 @@ public function buildHtml(array $processedValue): string */ $template = $domResult->firstChild; $generatedHtml = $domResult->saveXML($template); + if ($generatedHtml === false) { + return ''; + } return str_replace([''], '', $generatedHtml); } @@ -69,13 +75,13 @@ public function buildHtml(array $processedValue): string * in the following structure: `|, * for example, p|2 means 2nd element in current level and HTML tag

. * + * @param DOMNodeList $nodeList * @return array */ private function xmlToArray(DOMNodeList $nodeList, string $parentNodeName = ''): array { $result = []; $i = 0; - /** @var DOMNode $child */ foreach ($nodeList as $node) { $nodeNameAndLevel = sprintf('%s%s|%d', $parentNodeName ? sprintf('%s>', $parentNodeName) : '', $node->nodeName, $i); if ($node->hasChildNodes()) { @@ -84,7 +90,7 @@ private function xmlToArray(DOMNodeList $nodeList, string $parentNodeName = ''): $this->xmlToArray($node->childNodes, $nodeNameAndLevel) ); } else { - $result[$nodeNameAndLevel] = $node->nodeValue; + $result[$nodeNameAndLevel] = (string)$node->nodeValue; } $i++; } @@ -94,40 +100,49 @@ private function xmlToArray(DOMNodeList $nodeList, string $parentNodeName = ''): /** * Unflattens the processed array and makes it associative * for further processing + * + * @param array $result + * @param list $entryPointLevel + * @return array */ - private function addToResult(array $result, $entryPointLevel, string $value): array + private function addToResult(array $result, array $entryPointLevel, string $value): array { $currentEntryPointLevel = array_shift($entryPointLevel); if ($currentEntryPointLevel === null) { - $result = [$value]; - return $result; + return [$value]; } [$nodeType, $countInLevel] = explode('|', $currentEntryPointLevel); - $result[$countInLevel][$nodeType] ??= []; - $result[$countInLevel][$nodeType] = $this->addToResult($result[$countInLevel][$nodeType], $entryPointLevel, $value); + $subResult = $result[$countInLevel][$nodeType] ?? []; + if (!is_array($subResult)) { + $subResult = []; + } + $result[$countInLevel][$nodeType] = $this->addToResult($subResult, $entryPointLevel, $value); return $result; } /** * Adds all found nodes recursively to the DOM + * + * @param array $currentProcessing */ private function addToDomRecursive(DOMNode $parentNode, array $currentProcessing): void { foreach ($currentProcessing as $processingNode) { if (!is_array($processingNode)) { // last node, text only - $parentNode->nodeValue = $processingNode; + $parentNode->nodeValue = is_scalar($processingNode) ? (string)$processingNode : ''; return; } - $currentNodeType = array_keys($processingNode)[0]; + $currentNodeType = (string)(array_keys($processingNode)[0] ?? ''); if ($currentNodeType === '#text') { $currentNode = new DOMText(); } else { try { $currentNode = new DOMElement($currentNodeType); - } catch (\DOMException $e) { + } catch (\DOMException) { // @todo add more DOMNode properties + continue; } } $parentNode->appendChild($currentNode); diff --git a/Classes/ViewHelpers/Backend/Site/WritePresetViewHelper.php b/Classes/ViewHelpers/Backend/Site/WritePresetViewHelper.php index ac09cb2..29463cf 100644 --- a/Classes/ViewHelpers/Backend/Site/WritePresetViewHelper.php +++ b/Classes/ViewHelpers/Backend/Site/WritePresetViewHelper.php @@ -8,6 +8,7 @@ use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; use WebVision\DeeplWrite\Domain\Enum\RephraseSupportedDeepLLanguage; use WebVision\DeeplWrite\Domain\Enum\RephraseToneDeepL; @@ -22,7 +23,7 @@ public function __construct( ) { } - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('siteLanguageIds', 'string', 'Comma separated site languages', true); } @@ -31,7 +32,11 @@ public function render(): string { // @todo $siteLanguageIds is not used - ViewHelper argument make not sense. Should be rechecked. $siteLanguageIds = GeneralUtility::intExplode(',', $this->arguments['siteLanguageIds'], true); - $request = $this->renderingContext->getRequest(); + $renderingContext = $this->renderingContext; + if (!$renderingContext instanceof RenderingContext) { + return ''; + } + $request = $renderingContext->getRequest(); if (!$request instanceof ServerRequest) { return ''; } diff --git a/Core13/Event/Listener/DeeplWritePageViewRegistrationEventListener.php b/Core13/Event/Listener/DeeplWritePageViewRegistrationEventListener.php index 4be64f3..da371d6 100644 --- a/Core13/Event/Listener/DeeplWritePageViewRegistrationEventListener.php +++ b/Core13/Event/Listener/DeeplWritePageViewRegistrationEventListener.php @@ -95,6 +95,7 @@ public function __invoke(ModifyInjectVariablesViewHelperEvent $event): void } /** + * @param array $parameters * @throws RouteNotFoundException */ private function buildBackendRoute(string $route, array $parameters): string diff --git a/Core13/Generator/WriteDropdownGenerator.php b/Core13/Generator/WriteDropdownGenerator.php index 9ea756d..e34fa34 100644 --- a/Core13/Generator/WriteDropdownGenerator.php +++ b/Core13/Generator/WriteDropdownGenerator.php @@ -75,10 +75,6 @@ public function buildWriteDropdown( $output .= ''; } - if ($output === '') { - return ''; - } - return sprintf( '%s', htmlspecialchars($this->getLocalization()->sL('LLL:EXT:deepl_write/Resources/Private/Language/locallang.xlf:backend.label')), @@ -138,6 +134,7 @@ private function getLocalization(): LanguageService } /** + * @param array $parameters * @throws RouteNotFoundException */ private function buildBackendRoute(string $route, array $parameters): string diff --git a/composer.json b/composer.json index 1c1679f..1da4de3 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "phpstan/phpstan": "^1.12.33", "phpunit/phpunit": "^10.5", "saschaegerer/phpstan-typo3": "^1.10.2", + "typo3/cms-lowlevel": "^13.4", "typo3/testing-framework": "^8.3.1" }, "license": "GPL-2.0-or-later",