• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CPS-IT / handlebars-forms / 22340200704

24 Feb 2026 07:00AM UTC coverage: 0.0%. Remained the same
22340200704

push

github

eliashaeussler
[TASK] Improve namings and extract RENDERABLES to its own value processor

0 of 129 new or added lines in 18 files covered. (0.0%)

3 existing lines in 2 files now uncovered.

0 of 458 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Classes/DataProcessing/ProcessFormProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "handlebars_forms".
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17

18
namespace CPSIT\Typo3HandlebarsForms\DataProcessing;
19

20
use Psr\Http\Message;
21
use Psr\Log;
22
use Symfony\Component\DependencyInjection;
23
use TYPO3\CMS\Fluid;
24
use TYPO3\CMS\Form;
25
use TYPO3\CMS\Frontend;
26
use TYPO3Fluid\Fluid as FluidStandalone;
27

28
/**
29
 * ProcessFormProcessor
30
 *
31
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
32
 * @license GPL-2.0-or-later
33
 */
34
#[DependencyInjection\Attribute\AutoconfigureTag('data.processor', ['identifier' => 'process-form'])]
35
final readonly class ProcessFormProcessor implements Frontend\ContentObject\DataProcessorInterface
36
{
37
    private const CONTENT_PLACEHOLDER = '###FORM_CONTENT###';
38

39
    /**
40
     * @param DependencyInjection\ServiceLocator<Value\ValueProcessor> $valueProcessors
41
     */
42
    public function __construct(
×
43
        private Log\LoggerInterface $logger,
44
        private Fluid\Core\Rendering\RenderingContextFactory $renderingContextFactory,
45
        private Renderable\FormRenderableProcessor $formRenderableProcessor,
46
        #[DependencyInjection\Attribute\AutowireLocator('handlebars_forms.value_processor', defaultIndexMethod: 'getName')]
47
        private DependencyInjection\ServiceLocator $valueProcessors,
48
    ) {}
×
49

50
    /**
51
     * @param array<string, mixed> $contentObjectConfiguration
52
     * @param array<string, mixed> $processorConfiguration
53
     * @param array<string, mixed> $processedData
54
     * @return array<string, mixed>
55
     */
56
    public function process(
×
57
        Frontend\ContentObject\ContentObjectRenderer $cObj,
58
        array $contentObjectConfiguration,
59
        array $processorConfiguration,
60
        array $processedData,
61
    ): array {
62
        $formRuntime = $contentObjectConfiguration['variables.']['form'] ?? null;
×
63

64
        if (!($formRuntime instanceof Form\Domain\Runtime\FormRuntime)) {
×
65
            $this->logger->error(
×
66
                'Form runtime is not available when trying to process form with plugin uid "{uid}".',
×
67
                ['uid' => $cObj->data['uid'] ?? '(unknown)'],
×
68
            );
×
69

70
            return $processedData;
×
71
        }
72

73
        // Create and prepare Fluid rendering context
74
        $renderingContext = $this->renderingContextFactory->create();
×
75
        $renderingContext->setAttribute(Message\ServerRequestInterface::class, $formRuntime->getRequest());
×
76
        $renderingContext->getViewHelperVariableContainer()->addOrUpdate(
×
77
            Form\ViewHelpers\RenderRenderableViewHelper::class,
×
78
            'formRuntime',
×
79
            $formRuntime,
×
80
        );
×
81

82
        $this->formRenderableProcessor->process(
×
83
            $formRuntime,
×
84
            $renderingContext,
×
85
            function (FluidStandalone\Core\ViewHelper\TagBuilder $tagBuilder) use (
×
86
                $cObj,
×
87
                $formRuntime,
×
88
                &$processedData,
×
89
                $processorConfiguration,
×
90
                $renderingContext,
×
91
                &$tag,
×
92
            ) {
×
93
                $tag = $tagBuilder;
×
94
                $tag->setContent(self::CONTENT_PLACEHOLDER);
×
95

NEW
96
                $viewModel = new Renderable\RenderableViewModel($renderingContext, null, $tag);
×
NEW
97
                $processedData = $this->processRenderable($formRuntime, $processorConfiguration, $cObj, $viewModel) ?? [];
×
98

99
                return '';
×
100
            },
×
101
        );
×
102

103
        $inputFields = $tag?->getContent() ?? '';
×
104

105
        array_walk_recursive($processedData, static function (&$value) use ($inputFields) {
×
106
            if (is_string($value)) {
×
107
                $value = str_replace(self::CONTENT_PLACEHOLDER, $inputFields, $value);
×
108
            }
109
        });
×
110

111
        return $processedData;
×
112
    }
113

114
    /**
115
     * @param array<string, mixed> $configuration
116
     * @return array<string, mixed>|null
117
     */
118
    private function processRenderable(
×
119
        Form\Domain\Model\Renderable\RootRenderableInterface $renderable,
120
        array $configuration,
121
        Frontend\ContentObject\ContentObjectRenderer $cObj,
122
        Renderable\RenderableViewModel $viewModel,
123
    ): ?array {
124
        $processedData = [];
×
125

126
        // Early return on configured "if" condition evaluating to false
NEW
127
        if (!$this->checkIf($configuration, $renderable, $cObj, $viewModel)) {
×
128
            return null;
×
129
        }
130

131
        foreach ($configuration as $key => $value) {
×
132
            $keyWithoutDot = rtrim($key, '.');
×
133
            $keyWithDot = $keyWithoutDot . '.';
×
134

135
            if (is_array($value) && !array_key_exists($keyWithoutDot, $processedData)) {
×
NEW
136
                $processedValue = $this->processRenderable($renderable, $value, $cObj, $viewModel);
×
137

138
                if (is_array($processedValue)) {
×
139
                    $processedData[$keyWithoutDot] = $processedValue;
×
140
                }
141
            }
142

143
            if (!is_string($value)) {
×
144
                continue;
×
145
            }
146

147
            $valueConfiguration = $configuration[$keyWithDot] ?? [];
×
148

149
            // Process configured value
150
            if ($this->valueProcessors->has($value)) {
×
151
                $processedValue = $this->valueProcessors->get($value)->process(
×
152
                    $renderable,
×
NEW
153
                    $viewModel,
×
NEW
154
                    new Value\ProcessingContext(
×
NEW
155
                        $valueConfiguration,
×
NEW
156
                        fn(
×
NEW
157
                            array $contextConfiguration,
×
NEW
158
                            ?Form\Domain\Model\Renderable\RootRenderableInterface $contextRenderable = null,
×
NEW
159
                            ?Renderable\RenderableViewModel $contextViewModel = null,
×
NEW
160
                        ) => $this->processRenderable(
×
NEW
161
                            $contextRenderable ?? $renderable,
×
NEW
162
                            $contextConfiguration,
×
NEW
163
                            $cObj,
×
NEW
164
                            $contextViewModel ?? $viewModel,
×
NEW
165
                        ),
×
NEW
166
                    ),
×
UNCOV
167
                );
×
168
            } else {
169
                $processedValue = $value;
×
170
            }
171

172
            // Skip further processing if processed value is not a string (all COR related methods require a string value)
173
            if (!is_string($processedValue)) {
×
174
                $processedData[$keyWithoutDot] = $processedValue;
×
175
                continue;
×
176
            }
177

178
            // Process value with stdWrap
179
            if (is_array($valueConfiguration['stdWrap.'] ?? null)) {
×
180
                $processedValue = $cObj->stdWrap($processedValue, $valueConfiguration['stdWrap.']);
×
181
            }
182

183
            // Skip value if a configured "if" evaluates to false
184
            if (is_array($valueConfiguration['if.'] ?? null)) {
×
185
                $valueConfiguration['if.']['value'] ??= $processedValue;
×
186

187
                if (!$cObj->checkIf($valueConfiguration['if.'])) {
×
188
                    continue;
×
189
                }
190
            }
191

192
            $processedData[$keyWithoutDot] = $processedValue;
×
193
        }
194

195
        return $processedData;
×
196
    }
197

198
    /**
199
     * @param array<string, mixed> $configuration
200
     */
201
    private function checkIf(
×
202
        array &$configuration,
203
        Form\Domain\Model\Renderable\RootRenderableInterface $renderable,
204
        Frontend\ContentObject\ContentObjectRenderer $cObj,
205
        Renderable\RenderableViewModel $viewModel,
206
    ): bool {
207
        if (!is_array($configuration['if.'] ?? null)) {
×
208
            return true;
×
209
        }
210

211
        $cObjTemp = clone $cObj;
×
212

213
        if (is_string($configuration['if.']['value'] ?? null) && is_array($configuration['if.']['value.'] ?? null)) {
×
214
            $processedValue = $this->processRenderable(
×
NEW
215
                $renderable,
×
216
                [
×
217
                    'value' => $configuration['if.']['value'],
×
218
                    'value.' => $configuration['if.']['value.'],
×
219
                ],
×
220
                $cObj,
×
NEW
221
                $viewModel,
×
222
            );
×
223

224
            $cObjTemp->setCurrentVal($processedValue['value'] ?? null);
×
225

226
            unset($configuration['if.']['value'], $configuration['if.']['value.']);
×
227
        }
228

229
        if (!$cObjTemp->checkIf($configuration['if.'])) {
×
230
            return false;
×
231
        }
232

233
        unset($configuration['if.']);
×
234

235
        return true;
×
236
    }
237
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc