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

FluidTYPO3 / flux / 27757675993

18 Jun 2026 11:55AM UTC coverage: 89.162% (-3.5%) from 92.646%
27757675993

push

github

NamelessCoder
[TASK] Address last phpstan warnings

6228 of 6985 relevant lines covered (89.16%)

40.84 hits per line

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

94.38
/Classes/Form/AbstractFormComponent.php
1
<?php
2
namespace FluidTYPO3\Flux\Form;
3

4
/*
5
 * This file is part of the FluidTYPO3/Flux project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use FluidTYPO3\Flux\Enum\ExtensionOption;
12
use FluidTYPO3\Flux\Enum\FormOption;
13
use FluidTYPO3\Flux\Form;
14
use FluidTYPO3\Flux\Form\Container\Column;
15
use FluidTYPO3\Flux\Form\Container\Container;
16
use FluidTYPO3\Flux\Form\Container\Grid;
17
use FluidTYPO3\Flux\Form\Container\Section;
18
use FluidTYPO3\Flux\Form\Container\SectionObject;
19
use FluidTYPO3\Flux\Form\Container\Sheet;
20
use FluidTYPO3\Flux\Hooks\HookHandler;
21
use FluidTYPO3\Flux\Utility\ExtensionConfigurationUtility;
22
use FluidTYPO3\Flux\Utility\ExtensionNamingUtility;
23
use FluidTYPO3\Flux\Utility\RecursiveArrayUtility;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
26

27
abstract class AbstractFormComponent implements FormInterface
28
{
29
    public const string NAMESPACE_FIELD = 'FluidTYPO3\\Flux\\Form\\Field';
30
    public const string NAMESPACE_CONTAINER = 'FluidTYPO3\\Flux\\Form\\Container';
31

32
    protected ?string $name = null;
33
    protected bool $enabled = true;
34
    protected ?string $label = null;
35
    protected ?string $description = null;
36
    protected ?string $extensionName = 'FluidTYPO3.Flux';
37
    protected ?FormInterface $parent = null;
38
    protected array $variables = [];
39
    protected bool $inherit = false;
40
    protected bool $inheritEmpty = false;
41
    protected ?string $transform = null;
42

43
    /**
44
     * If TRUE, disables LLL label usage and always returns the
45
     * raw value of $label.
46
     */
47
    protected bool $disableLocalLanguageLabels = false;
48

49
    /**
50
     * Relative (from extension $extensionName) path to locallang
51
     * file containing labels for the LLL values built by this class.
52
     */
53
    protected string $localLanguageFileRelativePath = Form::DEFAULT_LANGUAGEFILE;
54

55
    public static function create(array $settings = []): FormInterface
56
    {
57
        $className = get_called_class();
320✔
58
        /** @var FormInterface $object */
59
        $object = GeneralUtility::makeInstance($className);
320✔
60
        $object->modify($settings);
320✔
61
        return HookHandler::trigger(HookHandler::FORM_COMPONENT_CREATED, ['component' => $object])['component'];
320✔
62
    }
63

64
    /**
65
     * @param string|class-string $type
66
     * @return class-string
67
     */
68
    protected function createComponentClassName(string $type, ?string $prefix): string
69
    {
70
        /** @var class-string $className */
71
        $className = str_replace('/', '\\', $type);
664✔
72
        $className = class_exists($prefix . '\\' . $className) ? $prefix . '\\' . $className : $className;
664✔
73
        /** @var class-string $className */
74
        $className = trim($className, '\\');
664✔
75
        return $className;
664✔
76
    }
77

78
    /**
79
     * @template T
80
     * @param class-string<T> $type
81
     * @return T&FieldInterface
82
     */
83
    public function createField(string $type, string $name, ?string $label = null): FieldInterface
84
    {
85
        /** @var T&FieldInterface $component */
86
        $component = $this->createComponent(static::NAMESPACE_FIELD, $type, $name, $label);
444✔
87
        return $component;
444✔
88
    }
89

90
    /**
91
     * @template T
92
     * @param class-string<T> $type
93
     * @return T&ContainerInterface
94
     */
95
    public function createContainer(string $type, string $name, ?string $label = null): ContainerInterface
96
    {
97
        /** @var T&ContainerInterface $component */
98
        $component = $this->createComponent(static::NAMESPACE_CONTAINER, $type, $name, $label);
188✔
99
        return $component;
188✔
100
    }
101

102
    public function createComponent(
103
        ?string $namespace,
104
        string $type,
105
        string $name,
106
        ?string $label = null
107
    ): FormInterface {
108
        /** @var FormInterface $component */
109
        $component = GeneralUtility::makeInstance($this->createComponentClassName($type, $namespace));
664✔
110
        $component->setName($name);
664✔
111
        $component->setLabel($label);
664✔
112
        $component->setLocalLanguageFileRelativePath($this->getLocalLanguageFileRelativePath());
664✔
113
        $component->setDisableLocalLanguageLabels($this->getDisableLocalLanguageLabels());
664✔
114
        $component->setExtensionName($this->getExtensionName());
664✔
115
        return HookHandler::trigger(HookHandler::FORM_COMPONENT_CREATED, ['component' => $component])['component'];
664✔
116
    }
117

118
    public function setTransform(?string $transform): self
119
    {
120
        $this->transform = $transform;
420✔
121
        if ($transform) {
420✔
122
            $root = $this->getRoot();
228✔
123
            if ($root instanceof Form) {
228✔
124
                $root->setOption(FormOption::TRANSFORM, true);
104✔
125
            }
126
        }
127
        return $this;
420✔
128
    }
129

130
    public function getTransform(): ?string
131
    {
132
        return $this->transform;
1,136✔
133
    }
134

135
    public function setName(string $name): self
136
    {
137
        $this->name = $name;
2,304✔
138
        return $this;
2,304✔
139
    }
140

141
    public function getName(): ?string
142
    {
143
        return $this->name;
1,768✔
144
    }
145

146
    public function getEnabled(): bool
147
    {
148
        return $this->enabled;
476✔
149
    }
150

151
    public function setEnabled(bool $enabled): self
152
    {
153
        $this->enabled = (bool) $enabled;
648✔
154
        return $this;
648✔
155
    }
156

157
    public function setExtensionName(?string $extensionName): self
158
    {
159
        $this->extensionName = $extensionName;
1,228✔
160
        return $this;
1,228✔
161
    }
162

163
    public function getExtensionName(): ?string
164
    {
165
        return $this->extensionName;
1,704✔
166
    }
167

168
    public function setDescription(?string $description): self
169
    {
170
        $this->description = $description;
260✔
171
        return $this;
260✔
172
    }
173

174
    public function getDescription(): ?string
175
    {
176
        return $this->description;
224✔
177
    }
178

179
    public function setLabel(?string $label): self
180
    {
181
        $this->label = $label;
2,268✔
182
        return $this;
2,268✔
183
    }
184

185
    public function getPath(): string
186
    {
187
        $prefix = '';
484✔
188
        if ($this instanceof Sheet) {
484✔
189
            $prefix = 'sheets';
40✔
190
        } elseif ($this instanceof Section) {
456✔
191
            $prefix = 'sections';
12✔
192
        } elseif ($this instanceof Grid) {
448✔
193
            $prefix = 'grids';
36✔
194
        } elseif ($this instanceof Column) {
432✔
195
            $prefix = 'columns';
52✔
196
        } elseif ($this instanceof SectionObject) {
396✔
197
            $prefix = 'objects';
16✔
198
        } elseif ($this instanceof Container) {
388✔
199
            $prefix = 'containers';
8✔
200
        } elseif ($this instanceof FieldInterface) {
380✔
201
            if ($this->isChildOfType('SectionObject')) {
356✔
202
                /** @var SectionObject $parent */
203
                $parent = $this->getParent();
80✔
204
                $prefix = 'objects.' . $parent->getName();
80✔
205
            } else {
206
                $prefix = 'fields';
280✔
207
            }
208
        }
209
        return trim($prefix . '.' . $this->getName(), '.');
484✔
210
    }
211

212
    public function getLabel(): ?string
213
    {
214
        return $this->resolveLocalLanguageValueOfLabel($this->label);
1,088✔
215
    }
216

217
    protected function resolveLocalLanguageValueOfLabel(?string $label, ?string $path = null): ?string
218
    {
219
        if ($this->getDisableLocalLanguageLabels()) {
1,100✔
220
            return $label;
104✔
221
        }
222

223
        $name = $this->getName();
996✔
224
        $extensionName = (string) $this->getExtensionName();
996✔
225

226
        if (empty($extensionName) && empty($label)) {
996✔
227
            return $name;
180✔
228
        }
229

230
        $extensionKey = ExtensionNamingUtility::getExtensionKey($extensionName);
816✔
231

232
        if (strpos($label ?? '', 'LLL:EXT:') === 0) {
816✔
233
            return $label;
24✔
234
        }
235

236
        $relativeFilePath = $this->getLocalLanguageFileRelativePath();
816✔
237
        $relativeFilePath = ltrim($relativeFilePath, '/');
816✔
238
        $filePrefix = 'LLL:EXT:' . $extensionKey . '/' . $relativeFilePath;
816✔
239
        if (strpos($label ?? '', 'LLL:') === 0 && strpos($label ?? '', ':') !== false) {
816✔
240
            // Shorthand LLL:name.of.index reference, expand
241
            [, $labelIdentifier] = explode(':', $label, 2);
100✔
242
            return $filePrefix . ':' . $labelIdentifier;
100✔
243
        } elseif (!empty($label)) {
716✔
244
            return $label;
236✔
245
        }
246
        if ($this instanceof Form) {
492✔
247
            return $filePrefix . ':flux.' . $this->getName();
16✔
248
        }
249
        $root = $this->getRoot();
476✔
250
        $id = $root->getName();
476✔
251
        if (empty($path)) {
476✔
252
            $path = $this->getPath();
460✔
253
        }
254
        return $filePrefix . ':' . trim('flux.' . $id . '.' . $path, '.');
476✔
255
    }
256

257
    public function setLocalLanguageFileRelativePath(string $localLanguageFileRelativePath): self
258
    {
259
        $this->localLanguageFileRelativePath = $localLanguageFileRelativePath;
704✔
260
        return $this;
704✔
261
    }
262

263
    public function getLocalLanguageFileRelativePath(): string
264
    {
265
        return $this->localLanguageFileRelativePath;
1,432✔
266
    }
267

268
    public function setDisableLocalLanguageLabels(bool $disableLocalLanguageLabels): self
269
    {
270
        $this->disableLocalLanguageLabels = (bool) $disableLocalLanguageLabels;
820✔
271
        return $this;
820✔
272
    }
273

274
    public function getDisableLocalLanguageLabels(): bool
275
    {
276
        return $this->disableLocalLanguageLabels;
1,716✔
277
    }
278

279
    public function setParent(?FormInterface $parent): self
280
    {
281
        $this->parent = $parent;
1,280✔
282
        return $this;
1,280✔
283
    }
284

285
    public function getParent(): ?FormInterface
286
    {
287
        return $this->parent;
732✔
288
    }
289

290
    public function setVariables(array $variables): self
291
    {
292
        $this->variables = (array) $variables;
452✔
293
        return $this;
452✔
294
    }
295

296
    public function getVariables(): array
297
    {
298
        return $this->variables;
104✔
299
    }
300

301
    /**
302
     * @param mixed $value
303
     */
304
    public function setVariable(string $name, $value): self
305
    {
306
        $this->variables = RecursiveArrayUtility::mergeRecursiveOverrule(
228✔
307
            $this->variables,
228✔
308
            RecursiveArrayUtility::convertPathToArray($name, $value)
228✔
309
        );
228✔
310
        return $this;
228✔
311
    }
312

313
    /**
314
     * @return mixed
315
     */
316
    public function getVariable(string $name)
317
    {
318
        return ObjectAccess::getPropertyPath($this->variables, $name);
248✔
319
    }
320

321
    /**
322
     * @return ContainerInterface|FormInterface|$this
323
     */
324
    public function getRoot(): FormInterface
325
    {
326
        $parent = $this->getParent();
704✔
327
        if (null === $parent || $this === $parent) {
704✔
328
            return $this;
704✔
329
        }
330
        return $parent->getRoot();
360✔
331
    }
332

333
    public function isChildOfType(string $type): bool
334
    {
335
        $parent = $this->getParent();
384✔
336
        if ($parent === null) {
384✔
337
            return false;
184✔
338
        }
339
        return (static::NAMESPACE_CONTAINER . '\\' . $type === get_class($parent) || is_a($parent, $type));
200✔
340
    }
341

342
    public function setInherit(bool $inherit): self
343
    {
344
        $this->inherit = $inherit;
316✔
345
        return $this;
316✔
346
    }
347

348
    public function getInherit(): bool
349
    {
350
        return $this->inherit;
128✔
351
    }
352

353
    public function setInheritEmpty(bool $inheritEmpty): self
354
    {
355
        $this->inheritEmpty = $inheritEmpty;
312✔
356
        return $this;
312✔
357
    }
358

359
    public function getInheritEmpty(): bool
360
    {
361
        return $this->inheritEmpty;
120✔
362
    }
363

364
    /**
365
     * Modifies the current Form Component by changing any properties
366
     * that were passed in $structure. If a component supports special
367
     * indices in $structure (for example a "fields" property) then
368
     * that component may specify its own `modify()` method and manually
369
     * process each of the specially supported keywords.
370
     *
371
     * For example, the AbstractFormContainer supports passing "fields"
372
     * and each field is then attempted fetched from children. If not
373
     * found, it is created (and the structure passed to the `create()`
374
     * function which uses the same structure syntax). If it already
375
     * exists, the `modify()` method is called on that object to trigger
376
     * the recursive modification of all child components.
377
     */
378
    public function modify(array $structure): self
379
    {
380
        if (isset($structure['options']) && is_array($structure['options'])) {
1,200✔
381
            foreach ($structure['options'] as $name => $value) {
104✔
382
                $this->setVariable($name, $value);
104✔
383
            }
384
            unset($structure['options']);
104✔
385
        }
386
        foreach ($structure as $propertyName => $propertyValue) {
1,200✔
387
            $setterMethodName = 'set' . ucfirst($propertyName);
696✔
388
            if (method_exists($this, $setterMethodName)) {
696✔
389
                /** @var \ReflectionParameter|null $parameterReflection */
390
                $parameterReflection = (new \ReflectionMethod($this, $setterMethodName))->getParameters()[0] ?? null;
676✔
391
                if ($parameterReflection === null) {
676✔
392
                    continue;
×
393
                }
394
                /** @var \ReflectionNamedType $typeReflection */
395
                $typeReflection = $parameterReflection->getType();
676✔
396
                if ($typeReflection) {
676✔
397
                    switch ($typeReflection->getName()) {
676✔
398
                        case 'bool':
676✔
399
                            $propertyValue = (bool) $propertyValue;
104✔
400
                            break;
104✔
401
                        case 'int':
572✔
402
                            $propertyValue = (int) $propertyValue;
8✔
403
                            break;
8✔
404
                        case 'array':
572✔
405
                            $propertyValue = is_array($propertyValue)
×
406
                                ? $propertyValue
×
407
                                : GeneralUtility::trimExplode(',', $propertyValue, true);
×
408
                            break;
×
409
                    }
410
                }
411

412
                $this->{$setterMethodName}($propertyValue);
676✔
413
            }
414
        }
415
        HookHandler::trigger(
1,200✔
416
            HookHandler::FORM_COMPONENT_MODIFIED,
1,200✔
417
            ['component' => $this, 'modififications' => $structure]
1,200✔
418
        );
1,200✔
419
        return $this;
1,200✔
420
    }
421

422
    /**
423
     * @param \SplObjectStorage|FormInterface[]|array $children
424
     */
425
    protected function buildChildren(iterable $children): array
426
    {
427
        /** @var OptionCarryingInterface $root */
428
        $root = $this->getRoot();
72✔
429

430
        $structure = [];
72✔
431
        foreach ($children as $child) {
72✔
432
            if (!$child->getEnabled() || $child instanceof FieldInterface && $child->isNative()) {
40✔
433
                continue;
×
434
            }
435
            $name = $child->getName();
40✔
436
            $configuration = $child->build();
40✔
437
            $config = $configuration['config'] ?? [];
40✔
438
            if (ExtensionConfigurationUtility::getOption(ExtensionOption::OPTION_UNIQUE_FILE_FIELD_NAMES)) {
40✔
439
                // Unique file names are enabled. IF the field is type=file or type=inline with FAL, prefix the name.
440
                $type = $config['type'] ?? null;
×
441
                if ($type === 'file' || ($type === 'inline' && $config['foreign_table'] === 'sys_file_reference')) {
×
442
                    $name = $root->getOption(FormOption::RECORD_FIELD) . '.' . $name;
×
443
                }
444
            }
445
            $structure[$name] = $configuration;
40✔
446
        }
447
        return $structure;
72✔
448
    }
449
}
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