diff --git a/Classes/Content/ContentTypeManager.php b/Classes/Content/ContentTypeManager.php index 39dd8f15f..bf19e9c36 100644 --- a/Classes/Content/ContentTypeManager.php +++ b/Classes/Content/ContentTypeManager.php @@ -83,7 +83,11 @@ public function registerTypeDefinition(ContentTypeDefinitionInterface $typeDefin public function determineContentTypeForTypeString(string $contentTypeName): ?ContentTypeDefinitionInterface { - return $this->types[$contentTypeName] ?? ($this->types[$contentTypeName] = $this->loadSingleDefinitionFromCache($contentTypeName)); + $definition = $this->loadSingleDefinitionFromCache($contentTypeName); + if ($definition === null) { + return null; + } + return $this->types[$contentTypeName] ?? ($this->types[$contentTypeName] = $definition); } public function determineContentTypeForRecord(array $record): ?ContentTypeDefinitionInterface diff --git a/Classes/Content/RuntimeDefinedContentProvider.php b/Classes/Content/RuntimeDefinedContentProvider.php index db2437fe6..a5a85fc17 100644 --- a/Classes/Content/RuntimeDefinedContentProvider.php +++ b/Classes/Content/RuntimeDefinedContentProvider.php @@ -77,7 +77,7 @@ public function trigger(array $row, $table, $field, $extensionKey = null) public function getControllerExtensionKeyFromRecord(array $row) { - return ExtensionNamingUtility::getExtensionKey($this->getExtensionKey($row)); + return ExtensionNamingUtility::getExtensionKey((string) $this->getExtensionKey($row)); } public function getControllerActionFromRecord(array $row) @@ -129,7 +129,7 @@ protected function getContentTypeDefinition(array $row): FluidRenderingContentTy 'Content type definition for %s must implement interface %s, class %s does not.', $row['CType'], FluidRenderingContentTypeDefinitionInterface::class, - get_class($definition) + $definition !== null ? get_class($definition) : '(unknown)' ), 1556109085 ); diff --git a/Classes/Content/TypeDefinition/FluidFileBased/FluidFileBasedContentTypeDefinition.php b/Classes/Content/TypeDefinition/FluidFileBased/FluidFileBasedContentTypeDefinition.php index 22e18d928..4638060db 100644 --- a/Classes/Content/TypeDefinition/FluidFileBased/FluidFileBasedContentTypeDefinition.php +++ b/Classes/Content/TypeDefinition/FluidFileBased/FluidFileBasedContentTypeDefinition.php @@ -77,11 +77,18 @@ public function getForm(array $record = []): Form $objectManager = GeneralUtility::makeInstance(ObjectManager::class); /** @var ProviderResolver $providerResolver */ $providerResolver = $objectManager->get(ProviderResolver::class); - return $providerResolver->resolvePrimaryConfigurationProvider( + $provider = $providerResolver->resolvePrimaryConfigurationProvider( 'tt_content', 'pi_flexform', $record - )->getForm($record); + ); + /** @var Form $defaultForm */ + $defaultForm = Form::create(); + + if ($provider === null) { + return $defaultForm; + } + return $provider->getForm($record) ?? $defaultForm; } public function getGrid(array $record = []): Form\Container\Grid @@ -90,11 +97,15 @@ public function getGrid(array $record = []): Form\Container\Grid $objectManager = GeneralUtility::makeInstance(ObjectManager::class); /** @var ProviderResolver $providerResolver */ $providerResolver = $objectManager->get(ProviderResolver::class); - return $providerResolver->resolvePrimaryConfigurationProvider( + $provider = $providerResolver->resolvePrimaryConfigurationProvider( 'tt_content', 'pi_flexform', $record - )->getGrid($record); + ); + if ($provider === null) { + return Form\Container\Grid::create(); + } + return $provider->getGrid($record); } public static function fetchContentTypes(): iterable diff --git a/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentGridProvider.php b/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentGridProvider.php index f0f19a13d..485693d64 100644 --- a/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentGridProvider.php +++ b/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentGridProvider.php @@ -10,6 +10,7 @@ use FluidTYPO3\Flux\Content\ContentGridForm; use FluidTYPO3\Flux\Content\ContentTypeManager; +use FluidTYPO3\Flux\Content\TypeDefinition\ContentTypeDefinitionInterface; use FluidTYPO3\Flux\Form\Container\Grid; use FluidTYPO3\Flux\Provider\AbstractProvider; use FluidTYPO3\Flux\Provider\Interfaces\GridProviderInterface; @@ -91,6 +92,10 @@ public function getForm(array $row) */ public function getGrid(array $row) { - return $this->contentTypeDefinitions->determineContentTypeForRecord($row)->getGrid() ?? parent::getGrid($row); + $contentTypeDefinition = $this->contentTypeDefinitions->determineContentTypeForRecord($row); + if (!($contentTypeDefinition instanceof ContentTypeDefinitionInterface)) { + return parent::getGrid($row); + } + return $contentTypeDefinition->getGrid() ?? parent::getGrid($row); } } diff --git a/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentTypeDefinition.php b/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentTypeDefinition.php index ee9553761..29c865c6d 100644 --- a/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentTypeDefinition.php +++ b/Classes/Content/TypeDefinition/RecordBased/RecordBasedContentTypeDefinition.php @@ -245,6 +245,9 @@ public function getTemplateSource(): string $columnTemplateChunk = '' . PHP_EOL; $grid = $this->getGrid(); + if (!($grid instanceof Grid)) { + return ''; + } $template = '
' . PHP_EOL; foreach ($grid->getRows() as $row) { $template .= '
' . PHP_EOL; diff --git a/Classes/Controller/AbstractFluxController.php b/Classes/Controller/AbstractFluxController.php index d1f648c01..62420ee11 100644 --- a/Classes/Controller/AbstractFluxController.php +++ b/Classes/Controller/AbstractFluxController.php @@ -8,6 +8,8 @@ * LICENSE.md file that was distributed with this source code. */ +use FluidTYPO3\Flux\Form; +use FluidTYPO3\Flux\Form\FormInterface; use FluidTYPO3\Flux\Hooks\HookHandler; use FluidTYPO3\Flux\Integration\NormalizedData\DataAccessTrait; use FluidTYPO3\Flux\Provider\Interfaces\ControllerProviderInterface; @@ -28,6 +30,7 @@ use TYPO3\CMS\Extbase\Mvc\Response; use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; use TYPO3\CMS\Fluid\View\TemplatePaths; +use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; /** @@ -164,16 +167,16 @@ protected function initializeOverriddenSettings() protected function initializeProvider() { $row = $this->getRecord(); - $table = $this->getFluxTableName(); + $table = (string) $this->getFluxTableName(); $field = $this->getFluxRecordField(); - $this->provider = $this->configurationService->resolvePrimaryConfigurationProvider( + $provider = $this->configurationService->resolvePrimaryConfigurationProvider( $table, $field, $row, null, ControllerProviderInterface::class ); - if (!$this->provider) { + if ($provider === null) { throw new \RuntimeException( 'Unable to resolve a ConfigurationProvider, but controller indicates it is a Flux-enabled ' . 'Controller - this is a grave error and indicates that EXT: ' . $this->extensionName . ' itself is ' . @@ -182,6 +185,7 @@ protected function initializeProvider() 1377458581 ); } + $this->provider = $provider; } /** @@ -354,6 +358,7 @@ protected function performSubRendering($extensionName, $controllerName, $actionN $shouldRelay = $this->hasSubControllerActionOnForeignController($extensionName, $controllerName, $actionName); $foreignControllerClass = null; + $content = null; if (!$shouldRelay) { if ($this->provider instanceof FluidProviderInterface) { $templatePathAndFilename = $this->provider->getTemplatePathAndFilename($this->getRecord()); @@ -385,7 +390,7 @@ protected function performSubRendering($extensionName, $controllerName, $actionN ); $content = $this->callSubControllerAction( $extensionName, - $foreignControllerClass, + $foreignControllerClass ?? static::class, $actionName, $pluginSignature ); @@ -512,7 +517,7 @@ protected function getData() } /** - * @return string + * @return string|null */ protected function getFluxRecordField() { @@ -520,7 +525,7 @@ protected function getFluxRecordField() } /** - * @return string + * @return string|null */ protected function getFluxTableName() { @@ -532,7 +537,11 @@ protected function getFluxTableName() */ public function getRecord() { - return $this->configurationManager->getContentObject()->data; + $contentObject = $this->configurationManager->getContentObject(); + if ($contentObject === null) { + throw new \UnexpectedValueException("Record of table " . $this->getFluxTableName() . ' not found', 1666538343); + } + return $contentObject->data; } /** @@ -541,10 +550,14 @@ public function getRecord() public function outletAction() { $record = $this->getRecord(); + $form = $this->provider->getForm($record); $input = $this->request->getArguments(); $targetConfiguration = $this->request->getInternalArguments()['__outlet']; - if ($this->provider->getTableName($record) !== $targetConfiguration['table'] - && $record['uid'] !== (integer) $targetConfiguration['recordUid'] + if ($form === null + || + ($this->provider->getTableName($record) !== $targetConfiguration['table'] + && $record['uid'] !== (integer) $targetConfiguration['recordUid'] + ) ) { // This instance does not match the instance that rendered the form. Forward the request // to the default "render" action. @@ -552,7 +565,8 @@ public function outletAction() } $input['settings'] = $this->settings; try { - $outlet = $this->provider->getForm($record)->getOutlet(); + /** @var Form $form */ + $outlet = $form->getOutlet(); $outlet->setView($this->view); $outlet->fill($input); if (!$outlet->isValid()) { diff --git a/Classes/Controller/PageController.php b/Classes/Controller/PageController.php index 1fea49d5a..42aa295bb 100644 --- a/Classes/Controller/PageController.php +++ b/Classes/Controller/PageController.php @@ -8,6 +8,7 @@ * LICENSE.md file that was distributed with this source code. */ +use FluidTYPO3\Flux\Provider\Interfaces\BasicProviderInterface; use FluidTYPO3\Flux\Service\FluxService; use FluidTYPO3\Flux\Service\PageService; use TYPO3\CMS\Extbase\Mvc\Web\Response; @@ -69,7 +70,12 @@ public function injectPageConfigurationService(FluxService $pageConfigurationSer */ protected function initializeProvider() { - $this->provider = $this->pageConfigurationService->resolvePageProvider($this->getRecord()); + $record = $this->getRecord(); + if ($record !== null) { + $provider = $this->pageConfigurationService->resolvePageProvider($record); + if ($provider instanceof BasicProviderInterface) + $this->provider = $provider; + } } /** diff --git a/Classes/Form.php b/Classes/Form.php index 035911f85..5fc9f7d04 100644 --- a/Classes/Form.php +++ b/Classes/Form.php @@ -103,7 +103,7 @@ public function initializeObject() /** * @param array $settings - * @return FormInterface + * @return static */ public static function create(array $settings = []) { @@ -129,7 +129,7 @@ public function add(Form\FormInterface $child) $this->children->rewind(); /** @var FormInterface|null $firstChild */ $firstChild = $this->children->count() > 0 ? $this->children->current() : null; - if ($this->children->count() === 1 && $firstChild->getName() === 'options' && !$firstChild->hasChildren()) { + if ($firstChild instanceof FormInterface && $this->children->count() === 1 && $firstChild->getName() === 'options' && !$firstChild->hasChildren()) { // Form has a single sheet, it's the default sheet and it has no fields. Replace it. $this->children->detach($this->children->current()); } diff --git a/Classes/Form/AbstractFormComponent.php b/Classes/Form/AbstractFormComponent.php index 707f8a6e1..951e79503 100644 --- a/Classes/Form/AbstractFormComponent.php +++ b/Classes/Form/AbstractFormComponent.php @@ -46,7 +46,7 @@ abstract class AbstractFormComponent implements FormInterface protected $enabled = true; /** - * @var string + * @var string|null */ protected $label = null; @@ -170,7 +170,7 @@ public function createWizard($type, $name, $label = null) * @param string $namespace * @param string|class-string $type * @param string $name - * @param string|NULL $label + * @param string|null $label * @return FormInterface */ public function createComponent($namespace, $type, $name, $label = null) @@ -266,7 +266,7 @@ public function getExtensionName() } /** - * @param string $label + * @param string|null $label * @return $this */ public function setLabel($label) @@ -304,7 +304,7 @@ public function getPath() } /** - * @return string + * @return string|null */ public function getLabel() { @@ -334,7 +334,7 @@ protected function resolveLocalLanguageValueOfLabel($label, $path = null) $relativeFilePath = $this->getLocalLanguageFileRelativePath(); $relativeFilePath = ltrim($relativeFilePath, '/'); $filePrefix = 'LLL:EXT:' . $extensionKey . '/' . $relativeFilePath; - if (strpos($label ?? '', 'LLL:') === 0) { + if (strpos($label ?? '', 'LLL:') === 0 && strpos($label ?? '', ':') !== false) { // Shorthand LLL:name.of.index reference, expand list (, $labelIdentifier) = explode(':', $label, 2); return $filePrefix . ':' . $labelIdentifier; @@ -535,6 +535,12 @@ public function modify(array $structure) unset($structure['options']); } foreach ($structure as $propertyName => $propertyValue) { + if ($propertyName === 'children') { + foreach ($propertyValue as $child) { + $this->add($child); + } + continue; + } $setterMethodName = 'set' . ucfirst($propertyName); if (true === method_exists($this, $setterMethodName)) { ObjectAccess::setProperty($this, $propertyName, $propertyValue); diff --git a/Classes/Form/AbstractFormContainer.php b/Classes/Form/AbstractFormContainer.php index 95ae67486..95c16834b 100644 --- a/Classes/Form/AbstractFormContainer.php +++ b/Classes/Form/AbstractFormContainer.php @@ -148,7 +148,7 @@ public function getChildren() } /** - * @return FormInterface|FALSE + * @return FormInterface|null */ public function last() { @@ -173,20 +173,21 @@ public function modify(array $structure) { if (isset($structure['fields']) || isset($structure['children'])) { $data = isset($structure['children']) ? $structure['children'] : $structure['fields']; - foreach ((array) $data as $index => $fieldData) { - $fieldName = true === isset($fieldData['name']) ? $fieldData['name'] : $index; + foreach ((array) $data as $index => $childData) { + $childName = true === isset($childData['name']) ? $childData['name'] : $index; // check if field already exists - if it does, modify it. If it does not, create it. - if (true === $this->has($fieldName)) { - /** @var FieldInterface|null $field */ - $field = $this->get($fieldName); + if (true === $this->has($childName)) { + /** @var FormInterface $child */ + $child = $this->get($childName); } else { - /** @var class-string $fieldType */ - $fieldType = true === isset($fieldData['type']) ? $fieldData['type'] : 'None'; - /** @var FieldInterface|null $field */ - $field = $this->createField($fieldType, $fieldName); + /** @var class-string $type */ + $type = true === isset($childData['type']) ? $childData['type'] : 'None'; + /** @var FormInterface $child */ + $child = $this->createField($type, $childName); } - $field->modify($fieldData); + + $child->modify($childData); } unset($structure['children'], $structure['fields']); } diff --git a/Classes/Form/AbstractInlineFormField.php b/Classes/Form/AbstractInlineFormField.php index 5914c3ab9..34adbbb63 100644 --- a/Classes/Form/AbstractInlineFormField.php +++ b/Classes/Form/AbstractInlineFormField.php @@ -470,7 +470,7 @@ public function getOverrideChildTca() */ public function setForeignTypes($foreignTypes) { - $this->foreignTypes = true === is_array($foreignTypes) ? $foreignTypes : null; + $this->foreignTypes = $foreignTypes ?? []; return $this; } diff --git a/Classes/Form/AbstractMultiValueFormField.php b/Classes/Form/AbstractMultiValueFormField.php index 1edf57a0e..624a5d4ff 100644 --- a/Classes/Form/AbstractMultiValueFormField.php +++ b/Classes/Form/AbstractMultiValueFormField.php @@ -225,7 +225,7 @@ public function getSelectedListStyle() } /** - * @return string + * @return string|null */ public function getRenderType() { diff --git a/Classes/Form/Container/Grid.php b/Classes/Form/Container/Grid.php index a55810475..c5f36760f 100644 --- a/Classes/Form/Container/Grid.php +++ b/Classes/Form/Container/Grid.php @@ -154,13 +154,13 @@ public function buildBackendLayout(int $parentRecordUid): BackendLayout $typoScriptString = ''; $root = $this->getRoot(); - $label = $root->getLabel(); + $label = (string) $root->getLabel(); foreach ($this->flattenSetup($configuration, 'backend_layout.') as $name => $value) { $typoScriptString .= $name . ' = ' . $value . LF; } return new BackendLayout( - $this->getRoot()->getName(), - LocalizationUtility::translate($label) + (string) $this->getRoot()->getName(), + (string) LocalizationUtility::translate($label) ? $label : 'LLL:EXT:flux/Resources/Private/Language/locallang.xlf:flux.grid.grids.grid', $typoScriptString diff --git a/Classes/Form/Container/Sheet.php b/Classes/Form/Container/Sheet.php index 3594e33bf..5cffd72f5 100644 --- a/Classes/Form/Container/Sheet.php +++ b/Classes/Form/Container/Sheet.php @@ -41,7 +41,7 @@ public function setShortDescription($shortDescription) } /** - * @return string + * @return string|null */ public function getShortDescription() { @@ -59,7 +59,7 @@ public function setDescription($description) } /** - * @return string + * @return string|null */ public function getDescription() { diff --git a/Classes/Form/Conversion/FormToFluidTemplateConverter.php b/Classes/Form/Conversion/FormToFluidTemplateConverter.php index 6181dad48..d2fc3b690 100644 --- a/Classes/Form/Conversion/FormToFluidTemplateConverter.php +++ b/Classes/Form/Conversion/FormToFluidTemplateConverter.php @@ -21,7 +21,7 @@ class FormToFluidTemplateConverter implements FormConverterInterface * @param array $configuration * @return string */ - public function convertFormAndGrid(Form $form, Form\Container\Grid $grid, array $configuration) + public function convertFormAndGrid(Form $form, ?Form\Container\Grid $grid, array $configuration) { $renderingTemplateChunk = $configuration[static::OPTION_TEMPLATE_SOURCE] ?? 'Hello world'; @@ -44,7 +44,7 @@ public function convertFormAndGrid(Form $form, Form\Container\Grid $grid, array TEMPLATE; $formFieldsChunk = $this->renderSheetsAndFields($form); - $gridChunk = $this->renderGrid($grid); + $gridChunk = $grid !== null ? $this->renderGrid($grid) : ''; $source = sprintf($template, $form->getId(), $formFieldsChunk, $gridChunk, $renderingTemplateChunk); diff --git a/Classes/Form/Field/ControllerActions.php b/Classes/Form/Field/ControllerActions.php index 1d8b914b1..5dadfef84 100644 --- a/Classes/Form/Field/ControllerActions.php +++ b/Classes/Form/Field/ControllerActions.php @@ -18,7 +18,6 @@ */ class ControllerActions extends Select { - /** * Name of the Extbase extension that contains the Controller * to parse, ex. MyExtension. In vendor based extensions use @@ -357,7 +356,7 @@ protected function buildExpectedAndExistingControllerClassName($controllerName) /** * @param string $controllerName * @param string $actionName - * @return string|NULL + * @return string */ protected function getLabelForControllerAction($controllerName, $actionName) { @@ -367,6 +366,9 @@ protected function getLabelForControllerAction($controllerName, $actionName) $pluginName = $this->getPluginName(); $separator = $this->getSeparator(); $controllerClassName = $this->buildExpectedAndExistingControllerClassName($controllerName); + if ($controllerClassName === null) { + return 'INVALID: ' . $controllerName . '->' . $actionName; + } $disableLocalLanguageLabels = $this->getDisableLocalLanguageLabels(); $labelPath = strtolower($pluginName . '.' . $controllerName . '.' . $actionName); $hasLocalLanguageFile = file_exists( diff --git a/Classes/Form/FormInterface.php b/Classes/Form/FormInterface.php index cb5e780c4..dbddcead3 100644 --- a/Classes/Form/FormInterface.php +++ b/Classes/Form/FormInterface.php @@ -52,13 +52,13 @@ public function getEnabled(); public function setEnabled($enabled); /** - * @param string $label + * @param string|null $label * @return $this */ public function setLabel($label); /** - * @return string + * @return string|null */ public function getLabel(); diff --git a/Classes/Integration/ContentTypeBuilder.php b/Classes/Integration/ContentTypeBuilder.php index 608f280e9..7bc1eddc7 100644 --- a/Classes/Integration/ContentTypeBuilder.php +++ b/Classes/Integration/ContentTypeBuilder.php @@ -45,7 +45,7 @@ class ContentTypeBuilder /** * @param string $providerExtensionName * @param string $templateFilename - * @param class-string|null $providerClassName + * @param class-string $providerClassName * @param string|null $contentType * @param string $defaultControllerExtensionName * @param string|null $controllerActionName @@ -54,7 +54,7 @@ class ContentTypeBuilder public function configureContentTypeFromTemplateFile( string $providerExtensionName, string $templateFilename, - ?string $providerClassName = Provider::class, + string $providerClassName = Provider::class, ?string $contentType = null, string $defaultControllerExtensionName = 'FluidTYPO3.Flux', ?string $controllerActionName = null @@ -77,7 +77,7 @@ public function configureContentTypeFromTemplateFile( $controllerClassName = str_replace('.', '\\', $defaultControllerExtensionName) . '\\Controller\\' . $controllerName . 'Controller'; $localControllerClassName = str_replace('.', '\\', $providerExtensionName) . '\\Controller\\' . $controllerName . 'Controller'; $extensionSignature = str_replace('_', '', ExtensionNamingUtility::getExtensionKey($providerExtensionName)); - $fullContentType = $contentType ?: $extensionSignature . '_' . strtolower($pluginName); + $fullContentType = $contentType ?: $extensionSignature . '_' . strtolower((string) $pluginName); if (!$this->validateContentController($localControllerClassName)) { class_alias($controllerClassName, $localControllerClassName); } @@ -174,7 +174,7 @@ public function registerContentType( // errors use the most base Exception class in PHP. So instead we check for a // specific dispatcher in the stack trace and re-throw if not matched. $pitcher = $error->getTrace()[0] ?? false; - if ($pitcher && ($pitcher['class'] ?? '') !== 'SplObjectStorage' && ($pitcher['function'] ?? '') !== 'serialize') { + if ($pitcher && ($pitcher['class'] ?? '') !== 'SplObjectStorage' && $pitcher['function'] !== 'serialize') { throw $error; } } @@ -230,7 +230,7 @@ protected function addPageTsConfig(Form $form, string $contentType): void $formId = $form->getId() ?: $contentType; $group = $form->getOption(Form::OPTION_GROUP); $groupName = $this->sanitizeString($group ?? 'fluxContent'); - $extensionName = $form->getExtensionName(); + $extensionName = $form->getExtensionName() ?? 'FluidTYPO3.Flux'; $extensionKey = ExtensionNamingUtility::getExtensionKey($extensionName); $labelSubReference = 'flux.newContentWizard.' . $groupName; @@ -268,7 +268,7 @@ protected function addPageTsConfig(Form $form, string $contentType): void protected function sanitizeString(string $string): string { $pattern = '/([^a-z0-9\-]){1,}/i'; - $replaced = preg_replace($pattern, '_', $string); + $replaced = (string) preg_replace($pattern, '_', $string); $replaced = trim($replaced, '_'); return empty($replaced) ? md5($string) : $replaced; } @@ -298,7 +298,7 @@ protected function registerExtbasePluginForForm(string $providerExtensionName, s ExtensionUtility::registerPlugin( $this->getExtensionIdentityForPluginRegistration($providerExtensionName), $this->getPluginNamePartFromContentType($contentType), - $form->getLabel(), + (string) $form->getLabel(), MiscellaneousUtility::getIconForTemplate($form), $providerExtensionName ); diff --git a/Classes/Integration/FormEngine/SiteConfigurationProviderItems.php b/Classes/Integration/FormEngine/SiteConfigurationProviderItems.php index a3ba25635..ed6bddf17 100644 --- a/Classes/Integration/FormEngine/SiteConfigurationProviderItems.php +++ b/Classes/Integration/FormEngine/SiteConfigurationProviderItems.php @@ -34,7 +34,7 @@ public function processPageTemplateItems(array $tca, TcaSelectItems $bar): array foreach ($pageService->getAvailablePageTemplateFiles() as $extensionName => $templateGroup) { foreach ($templateGroup as $form) { $templateFilename = $form->getOption(Form::OPTION_TEMPLATEFILE); - $label = $form->getLabel(); + $label = (string) $form->getLabel(); $identity = $extensionName . '->' . lcfirst(pathinfo($templateFilename, PATHINFO_FILENAME)); try { $label = LocalizationUtility::translate($label) ?: $identity; diff --git a/Classes/Integration/HookSubscribers/DataHandlerSubscriber.php b/Classes/Integration/HookSubscribers/DataHandlerSubscriber.php index cb9384c77..311b5c8cd 100644 --- a/Classes/Integration/HookSubscribers/DataHandlerSubscriber.php +++ b/Classes/Integration/HookSubscribers/DataHandlerSubscriber.php @@ -79,16 +79,18 @@ public function processDatamap_afterDatabaseOperations($command, $table, $id, $f if (!empty($fieldArray['l18n_parent'])) { // Command was "localize", read colPos value from the translation parent and use directly - $newColumnPosition = $this->getSingleRecordWithoutRestrictions($table, $fieldArray['l18n_parent'], 'colPos')['colPos']; + $newColumnPosition = $this->getSingleRecordWithoutRestrictions($table, $fieldArray['l18n_parent'], 'colPos')['colPos'] ?? null; } elseif (isset(static::$copiedRecords[$originalParentUid])) { // The parent of the original version of the record that was copied, was also copied in the same request; // this means the record that was copied, was copied as a recursion operation. Look up the most recent copy // of the original record's parent and create a new column position number based on the new parent. $newParentRecord = $this->getMostRecentCopyOfRecord($originalParentUid); - $newColumnPosition = ColumnNumberUtility::calculateColumnNumberForParentAndColumn( - $newParentRecord['uid'], - ColumnNumberUtility::calculateLocalColumnNumber($originalRecord['colPos']) - ); + if ($newParentRecord !== null) { + $newColumnPosition = ColumnNumberUtility::calculateColumnNumberForParentAndColumn( + $newParentRecord['uid'], + ColumnNumberUtility::calculateLocalColumnNumber($originalRecord['colPos']) + ); + } } elseif (($fieldArray['colPos'] ?? 0) >= ColumnNumberUtility::MULTIPLIER) { // Record is a child record, the updated field array still indicates it is a child (was not pasted outside // of parent, rather, parent was pasted somewhere else). @@ -96,14 +98,16 @@ public function processDatamap_afterDatabaseOperations($command, $table, $id, $f // right parent for the language and update the column position accordingly. $originalParentUid = ColumnNumberUtility::calculateParentUid($fieldArray['colPos']); $originalParent = $this->getSingleRecordWithoutRestrictions($table, $originalParentUid, 'sys_language_uid'); - if ($originalParent['sys_language_uid'] !== $fieldArray['sys_language_uid']) { + if ($originalParent !== null && $originalParent['sys_language_uid'] !== $fieldArray['sys_language_uid']) { // copyToLanguage case. Resolve the most recent translated version of the parent record in language of // child record, and calculate the new column position number based on it. $newParentRecord = $this->getTranslatedVersionOfParentInLanguageOnPage((int) $fieldArray['sys_language_uid'], (int) $fieldArray['pid'], (int) $originalParentUid); - $newColumnPosition = ColumnNumberUtility::calculateColumnNumberForParentAndColumn( - $newParentRecord['uid'], - ColumnNumberUtility::calculateLocalColumnNumber($fieldArray['colPos']) - ); + if ($newParentRecord !== null) { + $newColumnPosition = ColumnNumberUtility::calculateColumnNumberForParentAndColumn( + $newParentRecord['uid'], + ColumnNumberUtility::calculateLocalColumnNumber($fieldArray['colPos']) + ); + } } } @@ -165,7 +169,7 @@ public function processDatamap_preProcessFieldArray(array &$fieldArray, $table, // TODO: remove when expected solution, the inclusion of colPos in $fieldArray, is merged and released in TYPO3 if (!array_key_exists('colPos', $fieldArray)) { $record = $this->getSingleRecordWithoutRestrictions($table, (int) $id, 'pid, colPos, l18n_parent'); - $uidInDefaultLanguage = $record['l18n_parent']; + $uidInDefaultLanguage = $record['l18n_parent'] ?? null; if ($uidInDefaultLanguage && isset($dataHandler->datamap[$table][$uidInDefaultLanguage]['colPos'])) { $fieldArray['colPos'] = (integer) $dataHandler->datamap[$table][$uidInDefaultLanguage]['colPos']; } diff --git a/Classes/Integration/NormalizedData/Converter/InlineRecordDataConverter.php b/Classes/Integration/NormalizedData/Converter/InlineRecordDataConverter.php index e48ddcf56..976cb236e 100644 --- a/Classes/Integration/NormalizedData/Converter/InlineRecordDataConverter.php +++ b/Classes/Integration/NormalizedData/Converter/InlineRecordDataConverter.php @@ -149,6 +149,9 @@ protected function assertArrayHasKey(array $array, string $path): bool { $segments = GeneralUtility::trimExplode('.', $path); $lastSegment = array_pop($segments); + if ($lastSegment === null) { + return false; + } foreach ($segments as $segment) { $array = $array[$segment]; } diff --git a/Classes/Integration/NormalizedData/DataAccessTrait.php b/Classes/Integration/NormalizedData/DataAccessTrait.php index bca37aef9..fe1d9ec58 100644 --- a/Classes/Integration/NormalizedData/DataAccessTrait.php +++ b/Classes/Integration/NormalizedData/DataAccessTrait.php @@ -34,6 +34,9 @@ public function injectConfigurationManager(ConfigurationManagerInterface $config /** @var ObjectManagerInterface $objectManager */ $objectManager = GeneralUtility::makeInstance(ObjectManager::class); $contentObject = $this->configurationManager->getContentObject(); + if ($contentObject === null) { + throw new \UnexpectedValueException("Record of table " . $this->getFluxTableName() . ' not found', 1666538343); + } $table = $this->fluxTableName ?? $contentObject->getCurrentTable(); $field = $this->fluxRecordField ?? 'pi_flexform'; $record = $contentObject->data; diff --git a/Classes/Outlet/Pipe/TypeConverterPipe.php b/Classes/Outlet/Pipe/TypeConverterPipe.php index 9990b989d..07bfed2b1 100644 --- a/Classes/Outlet/Pipe/TypeConverterPipe.php +++ b/Classes/Outlet/Pipe/TypeConverterPipe.php @@ -90,7 +90,7 @@ public function getTargetType() return $this->targetType; } - public function getPropertyName(): string + public function getPropertyName(): ?string { return $this->propertyName; } diff --git a/Classes/Provider/AbstractProvider.php b/Classes/Provider/AbstractProvider.php index 07bf78e26..2b3316f6c 100644 --- a/Classes/Provider/AbstractProvider.php +++ b/Classes/Provider/AbstractProvider.php @@ -59,26 +59,26 @@ class AbstractProvider implements ProviderInterface /** * Fill with the name of the DB table which should trigger this Provider. * - * @var string + * @var string|null */ protected $tableName = null; /** * Fill with the "list_type" value that should trigger this Provider. * - * @var string + * @var string|null */ protected $listType = null; /** * Fill with the "CType" value that should trigger this Provider. * - * @var string + * @var string|null */ protected $contentObjectType = null; /** - * @var string + * @var string|null */ protected $parentFieldName = null; @@ -237,7 +237,7 @@ public function trigger(array $row, $table, $field, $extensionKey = null) * reading from template or overriding the getForm() method. * * @param array $row - * @return string + * @return class-string|null */ protected function resolveFormClassName(array $row) { @@ -255,7 +255,7 @@ protected function resolveFormClassName(array $row) */ protected function getViewVariables(array $row) { - $extensionKey = $this->getExtensionKey($row); + $extensionKey = (string) $this->getExtensionKey($row); $fieldName = $this->getFieldName($row); $variables = [ 'record' => $row, @@ -309,16 +309,22 @@ protected function createCustomFormInstance(array $row) */ public function getGrid(array $row) { + if ($this->grid instanceof Grid) { + return $this->grid; + } $form = $this->getForm($row); if ($form) { $container = $this->detectContentContainerParent($form); if ($container) { $values = $this->getFlexFormValues($row); - $persistedObjects = array_column( - ObjectAccess::getProperty($values, $container->getName()) ?? [], - $container->getContentContainer()->getName() - ); - + $contentContainer = $container->getContentContainer(); + $persistedObjects = []; + if ($contentContainer instanceof Form\Container\SectionObject) { + $persistedObjects = array_column( + ObjectAccess::getProperty($values, (string) $container->getName()) ?? [], + (string) $contentContainer->getName() + ); + } // Determine the mode to render, then create an ad-hoc grid. $grid = Grid::create(); @@ -347,7 +353,7 @@ public function getGrid(array $row) return $grid; } } - $grid = $this->grid ?? $this->extractConfiguration($row, 'grids')['grid'] ?? Grid::create(); + $grid = $this->extractConfiguration($row, 'grids')['grid'] ?? Grid::create(); $grid->setExtensionName($grid->getExtensionName() ?: $this->getControllerExtensionKeyFromRecord($row)); return $grid; } @@ -431,7 +437,7 @@ public function setListType($listType) } /** - * @return string + * @return string|null */ public function getListType() { @@ -440,6 +446,7 @@ public function getListType() /** * @param string $contentObjectType + * @return void */ public function setContentObjectType($contentObjectType) { @@ -447,7 +454,7 @@ public function setContentObjectType($contentObjectType) } /** - * @return string + * @return string|null */ public function getContentObjectType() { @@ -465,7 +472,7 @@ public function getFieldName(array $row) /** * @param array $row - * @return string + * @return string|null */ public function getParentFieldName(array $row) { @@ -475,7 +482,7 @@ public function getParentFieldName(array $row) /** * @param array $row The record row which triggered processing - * @return string|NULL + * @return string|null */ public function getTableName(array $row) { @@ -489,7 +496,7 @@ public function getTableName(array $row) */ public function getTemplatePathAndFilename(array $row) { - $templatePathAndFilename = $this->templatePathAndFilename; + $templatePathAndFilename = (string) $this->templatePathAndFilename; if (!PathUtility::isAbsolutePath($templatePathAndFilename)) { $templatePathAndFilename = GeneralUtility::getFileAbsFileName($templatePathAndFilename); if (true === empty($templatePathAndFilename)) { @@ -576,7 +583,7 @@ public function getTemplateVariables(array $row) $variables['record'] = $row; $variables['page'] = $this->getPageValues(); $variables['user'] = $GLOBALS['TSFE']->fe_user->user ?? []; - if (file_exists($this->getTemplatePathAndFilename($row))) { + if (file_exists((string) $this->getTemplatePathAndFilename($row))) { $variables['grid'] = $this->getGrid($row); $variables['form'] = $this->getForm($row); } @@ -634,7 +641,10 @@ public function preProcessRecord(array &$row, $id, DataHandler $reference) { // TODO: move to single-fire implementation in TceMain (DataHandler) $fieldName = $this->getFieldName($row); - $tableName = $this->getTableName($row); + if ($fieldName === null) { + return; + } + $tableName = (string) $this->getTableName($row); if (is_array($row[$fieldName]) && isset($row[$fieldName]['data']['options']['lDEF']) && is_array($row[$fieldName]['data']['options']['lDEF'])) { foreach ($row[$fieldName]['data']['options']['lDEF'] as $key => $value) { @@ -664,8 +674,9 @@ public function postProcessRecord($operation, $id, array &$row, DataHandler $ref { // TODO: move to single-fire implementation in TceMain (DataHandler) if ('update' === $operation || 'new' === $operation) { + $tableName = (string) $this->getTableName($row); $record = $reference->datamap[$this->tableName][$id]; - $stored = $this->recordService->getSingle($this->tableName, '*', $record['uid']) ?? $record; + $stored = $this->recordService->getSingle($tableName, '*', $record['uid']) ?? $record; $fieldName = $this->getFieldName((array) $record); $dontProcess = ( null === $fieldName @@ -695,7 +706,7 @@ public function postProcessRecord($operation, $id, array &$row, DataHandler $ref $row[$fieldName] = $stored[$fieldName]; $reference->datamap[$this->tableName][$id][$fieldName] = $row[$fieldName]; if ($stored['uid']) { - $this->recordService->update($this->tableName, $stored); + $this->recordService->update($tableName, $stored); } } } @@ -717,7 +728,7 @@ public function postProcessDatabaseOperation($status, $id, &$row, DataHandler $r // TODO: remove in Flux 10.0 // We dispatch the Outlet associated with the Form, triggering each defined // Pipe inside the Outlet to "conduct" the data. - $record = $this->recordService->getSingle($this->getTableName($row), '*', $id); + $record = $this->recordService->getSingle((string) $this->getTableName($row), '*', $id); if (null !== $record) { $form = $this->getForm($record); if (true === $form instanceof Form\FormInterface) { @@ -821,7 +832,7 @@ public function getViewForRecord(array $row, $viewClassName = TemplateView::clas { /** @var class-string $viewClassName */ - $controllerExtensionKey = $this->getControllerExtensionKeyFromRecord($row); + $controllerExtensionKey = $this->getControllerExtensionKeyFromRecord($row) ?? 'FluidTYPO3.Flux'; /** @var ObjectManagerInterface $objectManager */ $objectManager = GeneralUtility::makeInstance(ObjectManager::class); @@ -847,7 +858,7 @@ public function getViewForRecord(array $row, $viewClassName = TemplateView::clas $renderingContext = $objectManager->get(RenderingContext::class); $renderingContext->setControllerContext($controllerContext); $renderingContext->getTemplatePaths()->fillDefaultsByPackageName(ExtensionNamingUtility::getExtensionKey($controllerExtensionKey)); - $renderingContext->getTemplatePaths()->setTemplatePathAndFilename($this->getTemplatePathAndFilename($row)); + $renderingContext->getTemplatePaths()->setTemplatePathAndFilename((string) $this->getTemplatePathAndFilename($row)); $renderingContext->setControllerName($this->getControllerNameFromRecord($row)); $renderingContext->setControllerAction($this->getControllerActionFromRecord($row)); /** @var T $view */ @@ -925,7 +936,7 @@ public function getControllerNameFromRecord(array $row) * Stub: Get the extension key of the controller associated with $row * * @param array $row - * @return string + * @return string|null */ public function getControllerExtensionKeyFromRecord(array $row) { @@ -940,7 +951,7 @@ public function getControllerExtensionKeyFromRecord(array $row) */ public function getControllerPackageNameFromRecord(array $row) { - $extensionKey = $this->getControllerExtensionKeyFromRecord($row); + $extensionKey = $this->getControllerExtensionKeyFromRecord($row) ?? 'FluidTYPO3.Flux'; $extensionName = ExtensionNamingUtility::getExtensionName($extensionKey); $vendor = ExtensionNamingUtility::getVendorName($extensionKey); return null !== $vendor ? $vendor . '.' . $extensionName : $extensionName; @@ -1019,12 +1030,12 @@ public function setControllerAction($controllerAction) } /** - * @param array|NULL $templateVariables + * @param array|null $templateVariables * @return ProviderInterface */ public function setTemplateVariables($templateVariables) { - $this->templateVariables = $templateVariables; + $this->templateVariables = $templateVariables ?? []; return $this; } @@ -1039,7 +1050,7 @@ public function setTemplatePathAndFilename($templatePathAndFilename) } /** - * @param array|NULL $templatePaths + * @param array|null $templatePaths * @return ProviderInterface */ public function setTemplatePaths($templatePaths) @@ -1049,7 +1060,7 @@ public function setTemplatePaths($templatePaths) } /** - * @param string|NULL $configurationSectionName + * @param string|null $configurationSectionName * @return ProviderInterface */ public function setConfigurationSectionName($configurationSectionName) diff --git a/Classes/Provider/Interfaces/FluidProviderInterface.php b/Classes/Provider/Interfaces/FluidProviderInterface.php index 4ba7caed1..bf30b0878 100644 --- a/Classes/Provider/Interfaces/FluidProviderInterface.php +++ b/Classes/Provider/Interfaces/FluidProviderInterface.php @@ -21,7 +21,7 @@ interface FluidProviderInterface * field and sheets configuration. EXT:myext... syntax allowed * * @param array $row The record which triggered the processing - * @return string|NULL + * @return string|null */ public function getTemplatePathAndFilename(array $row); @@ -30,7 +30,7 @@ public function getTemplatePathAndFilename(array $row); * FlexForm configuration * * @param array $row The record which triggered the processing - * @return array|NULL + * @return array */ public function getTemplateVariables(array $row); diff --git a/Classes/Provider/Interfaces/RecordProviderInterface.php b/Classes/Provider/Interfaces/RecordProviderInterface.php index e3ec60b8b..d3f8a86ff 100644 --- a/Classes/Provider/Interfaces/RecordProviderInterface.php +++ b/Classes/Provider/Interfaces/RecordProviderInterface.php @@ -26,8 +26,8 @@ interface RecordProviderInterface * * @param array $row * @param string $table - * @param string $field - * @param string $extensionKey + * @param string|null $field + * @param string|null $extensionKey * @return boolean */ public function trigger(array $row, $table, $field, $extensionKey = null); diff --git a/Classes/Provider/PageLanguageOverlayProvider.php b/Classes/Provider/PageLanguageOverlayProvider.php index 67542dab8..1e3d0d8d3 100644 --- a/Classes/Provider/PageLanguageOverlayProvider.php +++ b/Classes/Provider/PageLanguageOverlayProvider.php @@ -34,11 +34,17 @@ protected function loadRecordTreeFromDatabase($record) $record[$parentFieldName] = $this->getParentFieldValue($record); } $pageRecord = $this->recordService->getSingle('pages', '*', $record['pid']); + if ($pageRecord === null) { + return []; + } $records = []; - while (0 < $pageRecord[$parentFieldName]) { + while ($parentFieldName !== null && $pageRecord !== null && 0 < ($pageRecord[$parentFieldName] ?? null)) { $record = $this->recordService->get($this->tableName, '*', 'pid = ' . $pageRecord['pid']); + if ($record === null) { + break; + } $parentFieldName = $this->getParentFieldName($record); - array_push($records, $record); + $records[] = $record; $pageRecord = $this->recordService->getSingle('pages', '*', $pageRecord['pid']); } $records = array_reverse($records); diff --git a/Classes/Provider/PageProvider.php b/Classes/Provider/PageProvider.php index c8f4e1d4b..d9d6a9eb4 100644 --- a/Classes/Provider/PageProvider.php +++ b/Classes/Provider/PageProvider.php @@ -142,7 +142,7 @@ public function getExtensionKey(array $row) /** * @param array $row - * @return string + * @return string|null */ public function getTemplatePathAndFilename(array $row) { @@ -246,8 +246,8 @@ public function getFlexFormValuesSingle(array $row) public function postProcessRecord($operation, $id, array &$row, DataHandler $reference, array $removals = []) { if ('update' === $operation) { - $record = $this->recordService->getSingle($this->getTableName($row), '*', $id); - if (!is_array($record)) { + $record = $this->recordService->getSingle((string) $this->getTableName($row), '*', $id); + if ($record === null) { return; } if (isset($reference->datamap[$this->tableName][$id])) { @@ -260,8 +260,8 @@ public function postProcessRecord($operation, $id, array &$row, DataHandler $ref if ($form) { $tableFieldName = $this->getFieldName($record); foreach ($form->getFields() as $field) { - $fieldName = $field->getName(); - $sheetName = $field->getParent()->getName(); + $fieldName = (string) $field->getName(); + $sheetName = (string) $field->getParent()->getName(); $inherit = (boolean) $field->getInherit(); $inheritEmpty = (boolean) $field->getInheritEmpty(); if (isset($record[$tableFieldName]['data']) && is_array($record[$tableFieldName]['data'])) { @@ -315,7 +315,7 @@ protected function setDefaultValuesInFieldsWithInheritedValues(Form $form, array { $inheritedConfiguration = $this->getInheritedConfiguration($row); foreach ($form->getFields() as $field) { - $name = $field->getName(); + $name = (string) $field->getName(); $inheritedValue = $this->getInheritedPropertyValueByDottedPath($inheritedConfiguration, $name); if (null !== $inheritedValue && true === $field instanceof Form\FieldInterface) { $field->setDefault($inheritedValue); @@ -403,9 +403,9 @@ protected function getParentFieldValue(array $row) { $parentFieldName = $this->getParentFieldName($row); if (null !== $parentFieldName && false === isset($row[$parentFieldName])) { - $row = $this->recordService->getSingle($this->getTableName($row), '*', $row[$parentFieldName]); + $row = $this->recordService->getSingle((string) $this->getTableName($row), '*', $row[$parentFieldName]); } - return $row[$parentFieldName]; + return $row[$parentFieldName] ?? null; } /** diff --git a/Classes/Provider/SubPageLanguageOverlayProvider.php b/Classes/Provider/SubPageLanguageOverlayProvider.php index ed7258598..58e7670fe 100644 --- a/Classes/Provider/SubPageLanguageOverlayProvider.php +++ b/Classes/Provider/SubPageLanguageOverlayProvider.php @@ -30,14 +30,17 @@ class SubPageLanguageOverlayProvider extends PageLanguageOverlayProvider impleme /** * @param array $row - * @return string + * @return string|null */ public function getControllerActionReferenceFromRecord(array $row) { $pageRow = $this->recordService->getSingle('pages', '*', $row['pid']); + if ($pageRow === null) { + return null; + } if (true === empty($pageRow[self::FIELD_ACTION_SUB])) { $pageRow = $this->pageService->getPageTemplateConfiguration($pageRow['uid']); } - return $pageRow[self::FIELD_ACTION_SUB]; + return $pageRow[self::FIELD_ACTION_SUB] ?? null; } } diff --git a/Classes/Service/FluxService.php b/Classes/Service/FluxService.php index 074e74844..0bb1548d9 100644 --- a/Classes/Service/FluxService.php +++ b/Classes/Service/FluxService.php @@ -22,6 +22,7 @@ use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException; use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\Log\LogManager; +use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\SingletonInterface; @@ -336,7 +337,9 @@ public function convertFileReferenceToTemplatePathAndFilename($reference) $parts = explode(':', $reference); $filename = array_pop($parts); if (true === ctype_digit($filename)) { - return $this->resourceFactory->getFileObjectFromCombinedIdentifier($reference)->getIdentifier(); + /** @var File $file */ + $file = $this->resourceFactory->getFileObjectFromCombinedIdentifier($reference); + return $file->getIdentifier(); } $reference = GeneralUtility::getFileAbsFileName($reference); return $reference; diff --git a/Classes/Service/PageService.php b/Classes/Service/PageService.php index 80b5ff092..fbe343a1e 100755 --- a/Classes/Service/PageService.php +++ b/Classes/Service/PageService.php @@ -167,7 +167,7 @@ public function getPageFlexFormSource($pageUid) $resolveParentPageUid = (integer) (0 > $page['pid'] ? $page['t3ver_oid'] : $page['pid']); $page = $this->workspacesAwareRecordService->getSingle('pages', $fieldList, $resolveParentPageUid); } - return $page['tx_fed_page_flexform']; + return $page['tx_fed_page_flexform'] ?? null; } /** diff --git a/Classes/Utility/CompatibilityRegistry.php b/Classes/Utility/CompatibilityRegistry.php index 0fc7f6a15..261dfdff8 100644 --- a/Classes/Utility/CompatibilityRegistry.php +++ b/Classes/Utility/CompatibilityRegistry.php @@ -194,7 +194,7 @@ public static function registerFeatureFlags($scope, array $versionedFeatureFlags /** * @param string $scope - * @param string|NULL $version + * @param string $version * @param mixed $default * @return mixed */ diff --git a/Classes/Utility/ExtensionNamingUtility.php b/Classes/Utility/ExtensionNamingUtility.php index ea31b751f..d27ebd26d 100644 --- a/Classes/Utility/ExtensionNamingUtility.php +++ b/Classes/Utility/ExtensionNamingUtility.php @@ -52,7 +52,7 @@ public static function getExtensionKey($qualifiedExtensionName) public static function getExtensionName($qualifiedExtensionName) { list(, $extensionName) = static::getVendorNameAndExtensionName($qualifiedExtensionName); - return $extensionName; + return (string) $extensionName; } /** diff --git a/Classes/Utility/MiscellaneousUtility.php b/Classes/Utility/MiscellaneousUtility.php index 8f4d4b3a8..5ace82d64 100644 --- a/Classes/Utility/MiscellaneousUtility.php +++ b/Classes/Utility/MiscellaneousUtility.php @@ -8,6 +8,9 @@ * LICENSE.md file that was distributed with this source code. */ +use DOMElement; +use DOMNode; +use DOMNodeList; use FluidTYPO3\Flux\Form; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider; @@ -43,7 +46,7 @@ public static function getIconForTemplate(Form $form) return $form->getOption(Form::OPTION_ICON); } if (true === $form->hasOption(Form::OPTION_TEMPLATEFILE)) { - $extensionKey = ExtensionNamingUtility::getExtensionKey($form->getExtensionName()); + $extensionKey = ExtensionNamingUtility::getExtensionKey((string) $form->getExtensionName()); $fullTemplatePathAndName = $form->getOption(Form::OPTION_TEMPLATEFILE); $templatePathParts = explode('/', $fullTemplatePathAndName); $templateName = pathinfo(array_pop($templatePathParts), PATHINFO_FILENAME); @@ -113,28 +116,30 @@ public static function cleanFlexFormXml($xml, array $removals = []) $dom->formatOutput = true; $fieldNodesToRemove = []; foreach ($dom->getElementsByTagName('field') as $fieldNode) { - /** @var \DOMElement $fieldNode */ + /** @var DOMElement $fieldNode */ if (true === in_array($fieldNode->getAttribute('index'), $removals)) { $fieldNodesToRemove[] = $fieldNode; } } foreach ($fieldNodesToRemove as $fieldNodeToRemove) { - /** @var \DOMElement $fieldNodeToRemove */ - $fieldNodeToRemove->parentNode->removeChild($fieldNodeToRemove); + /** @var DOMNode $parent */ + $parent = $fieldNodeToRemove->parentNode; + /** @var DOMElement $fieldNodeToRemove */ + $parent->removeChild($fieldNodeToRemove); } // Assign a hidden ID to all container-type nodes, making the value available in templates etc. foreach ($dom->getElementsByTagName('el') as $containerNode) { - /** @var \DOMElement $containerNode */ + /** @var DOMElement $containerNode */ $hasIdNode = false; - if (0 < $containerNode->attributes->length) { + if ($containerNode->attributes instanceof \DOMNamedNodeMap && 0 < $containerNode->attributes->length) { // skip tags reserved for other purposes by attributes; only allow pure tags. continue; } foreach ($containerNode->childNodes as $fieldNodeInContainer) { - /** @var \DOMNode $fieldNodeInContainer */ - if (false === $fieldNodeInContainer instanceof \DOMElement) { + /** @var DOMNode $fieldNodeInContainer */ + if (false === $fieldNodeInContainer instanceof DOMElement) { continue; } $isFieldNode = ('field' === $fieldNodeInContainer->tagName); @@ -167,19 +172,25 @@ public static function cleanFlexFormXml($xml, array $removals = []) } foreach ($nodesToBeRemoved as $node) { - /** @var \DOMElement $node */ - $node->parentNode->removeChild($node); + /** @var DOMNode $parent */ + $parent = $node->parentNode; + /** @var DOMElement $node */ + $parent->removeChild($node); } // Return empty string in case remaining flexform XML is all empty - $dataNode = $dom->getElementsByTagName('data')->item(0); - if (0 === $dataNode->getElementsByTagName('sheet')->length) { + /** @var DOMNodeList $dataNodes */ + $dataNodes = $dom->getElementsByTagName('data'); + /** @var DOMElement $dataNode */ + $dataNode = $dataNodes->item(0); + $elements = $dataNode->getElementsByTagName('sheet'); + if (0 === $elements->length) { return ''; } $xml = (string) $dom->saveXML(); // hack-like pruning of empty-named node inserted when removing objects from a previously populated Section - $xml = preg_replace('#\s*#', '', $xml); - $xml = preg_replace('#\s*#', '', $xml); + $xml = (string) preg_replace('#\s*#', '', $xml); + $xml = (string) preg_replace('#\s*#', '', $xml); return $xml; } } diff --git a/Classes/ViewHelpers/Form/DataViewHelper.php b/Classes/ViewHelpers/Form/DataViewHelper.php index 6fbea2eb0..8cd9a8676 100644 --- a/Classes/ViewHelpers/Form/DataViewHelper.php +++ b/Classes/ViewHelpers/Form/DataViewHelper.php @@ -179,7 +179,7 @@ public static function renderStatic( protected static function readDataArrayFromProvidersOrUsingDefaultMethod(array $providers, $record, $field) { if (0 === count($providers)) { - $dataArray = static::$configurationService->convertFlexFormContentToArray($record[$field]); + $dataArray = static::getFluxService()->convertFlexFormContentToArray($record[$field]); } else { $dataArray = []; /** @var ProviderInterface $provider */ diff --git a/Tests/Unit/Controller/AbstractFluxControllerTestCase.php b/Tests/Unit/Controller/AbstractFluxControllerTestCase.php index 247b51314..c7d76d134 100644 --- a/Tests/Unit/Controller/AbstractFluxControllerTestCase.php +++ b/Tests/Unit/Controller/AbstractFluxControllerTestCase.php @@ -518,8 +518,9 @@ public function testOutletActionForwardsUnmatchedConfigurationToRenderAction() $subject->expects($this->once())->method('forward')->with('render')->willThrowException(new StopActionException()); $subject->expects($this->once())->method('getRecord')->willReturn([]); $request = new Request(); - $provider = $this->getMockBuilder(Provider::class)->setMethods(['getTableName'])->getMock(); + $provider = $this->getMockBuilder(Provider::class)->setMethods(['getTableName', 'getForm'])->getMock(); $provider->expects($this->once())->method('getTableName')->willReturn('foobar'); + $provider->expects($this->once())->method('getForm')->willReturn(Form::create()); ObjectAccess::setProperty($request, 'internalArguments', ['outlet' => ['table' => 'xyz', 'uid' => 321]], true); ObjectAccess::setProperty($subject, 'request', $request, true); ObjectAccess::setProperty($subject, 'provider', $provider, true); diff --git a/Tests/Unit/Controller/ContentControllerTest.php b/Tests/Unit/Controller/ContentControllerTest.php index 672e4e718..685cbd9d1 100644 --- a/Tests/Unit/Controller/ContentControllerTest.php +++ b/Tests/Unit/Controller/ContentControllerTest.php @@ -32,9 +32,6 @@ public function canRegisterCustomControllerForContent() */ protected function createAndTestDummyControllerInstance() { - $record = Records::$contentRecordWithoutParentAndWithoutChildren; - $record['pi_flexform'] = Xml::SIMPLE_FLEXFORM_SOURCE_DEFAULT_SHEET_ONE_FIELD; - $record['tx_fed_fcefile'] = 'Flux:Default.html'; $this->performDummyRegistration(); $controllerClassName = 'FluidTYPO3\\Flux\\Controller\\ContentController'; /** @var AbstractFluxController $instance */ diff --git a/Tests/Unit/ViewHelpers/Content/GetViewHelperTest.php b/Tests/Unit/ViewHelpers/Content/GetViewHelperTest.php index 2d583cca8..5d0c48d00 100644 --- a/Tests/Unit/ViewHelpers/Content/GetViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/Content/GetViewHelperTest.php @@ -13,6 +13,7 @@ use FluidTYPO3\Flux\Form\Container\Grid; use FluidTYPO3\Flux\Form\Container\Row; use FluidTYPO3\Flux\Provider\Provider; +use FluidTYPO3\Flux\Provider\ProviderInterface; use FluidTYPO3\Flux\Tests\Fixtures\Data\Records; use FluidTYPO3\Flux\Tests\Unit\ViewHelpers\AbstractViewHelperTestCase; use FluidTYPO3\Flux\ViewHelpers\FormViewHelper; @@ -56,6 +57,7 @@ public function canRenderViewHelper() $node = $this->createNode('Text', 'Hello loopy world!'); $viewHelper = $this->buildViewHelperInstance($arguments, $variables, $node); $renderingContext = ObjectAccess::getProperty($viewHelper, 'renderingContext', true); + /** @var ProviderInterface $provider */ $provider = $this->objectManager->get(Provider::class); $provider->setGrid(Grid::create(['children' => [['type' => Row::class, 'children' => [['type' => Column::class, 'name' => 'void']]]]])); $provider->setForm(Form::create()); diff --git a/composer.json b/composer.json index 9267a0bf1..3261e1dd8 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "php": "^7.4.0 || ^8", "ext-json": "*", "typo3/cms-core": "^8.7 || ^9 || ^10 || ^11 || dev-master", + "ext-dom": "*", "typo3/cms-fluid": "^8.7 || ^9 || ^10 || ^11 || dev-master", "typo3/cms-backend": "^8.7 || ^9 || ^10 || ^11 || dev-master", "typo3/cms-frontend": "^8.7 || ^9 || ^10 || ^11 || dev-master", diff --git a/phpstan.neon b/phpstan.neon index c2605304d..586c8e08e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,7 +2,7 @@ includes: - phpstan-baseline.neon parameters: - level: 7 + level: 8 reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false ignoreErrors: @@ -34,6 +34,7 @@ parameters: - "#^Cannot call method (rowCount|fetch|fetchAssociative|fetchAll|fetchAllAssociative)\\(\\) on (Doctrine\\\\DBAL\\\\Driver\\\\Statement|Doctrine\\\\DBAL\\\\Driver\\\\ResultStatement|Doctrine\\\\DBAL\\\\Result)\\|int\\.$#" - "#^Static method TYPO3\\\\CMS\\\\Extbase\\\\Reflection\\\\ObjectAccess\\:\\:getProperty\\(\\) invoked with 3 parameters, 2 required\\.$#" - "#^Call to an undefined static method TYPO3\\\\CMS\\\\Backend\\\\Utility\\\\BackendUtility\\:\\:getMovePlaceholder\\(\\)\\.$#" + - "#^Strict comparison using === between TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\ContentObjectRenderer and null will always evaluate to false\\.$#" bootstrapFiles: - phpstan-bootstrap.php paths: