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

nepada / file-upload-control / 11552144244

28 Oct 2024 10:34AM UTC coverage: 81.633% (-8.7%) from 90.293%
11552144244

push

github

xificurk
Cleanup imported code

12 of 31 new or added lines in 3 files covered. (38.71%)

32 existing lines in 2 files now uncovered.

720 of 882 relevant lines covered (81.63%)

0.82 hits per line

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

31.54
/src/FileUploadControl/BaseControl.php
1
<?php
2
declare(strict_types = 1);
3

4
namespace Nepada\FileUploadControl;
5

6
use Nette;
7
use Nette\Forms\Control;
8
use Nette\Forms\Form;
9
use Nette\Forms\Rules;
10
use Nette\Localization\Translator;
11
use Nette\Utils\Html;
12
use Stringable;
13
use function array_unique;
14
use function explode;
15
use function get_parent_class;
16
use function implode;
17
use function is_array;
18
use function sprintf;
19
use function str_contains;
20

21
/**
22
 * Adapted from nette/forms BaseControl of the Nette Framework (https://nette.org)
23
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
24
 *
25
 * @license    BSD-3-Clause
26
 * @link       https://github.com/nette/forms
27
 * @property-read Form $form
28
 * @property-read string $htmlName
29
 * @property   string|bool|null $htmlId
30
 * @property   mixed $value
31
 * @property   string|Stringable|null $caption
32
 * @property   bool $disabled
33
 * @property   bool $omitted
34
 * @property-read Html $control
35
 * @property-read Html $label
36
 * @property-read Html $controlPrototype
37
 * @property-read Html $labelPrototype
38
 * @property   bool $required
39
 * @property-read bool $filled
40
 * @property-read array<string|Stringable> $errors
41
 * @property-read array<mixed> $options
42
 * @property-read string $error
43
 */
44
abstract class BaseControl extends Nette\Application\UI\Control implements Control
45
{
46

47
    public static string $idMask = 'frm-%s';
48

49
    /**
50
     * @var array<string, array<string, callable>> extension methods
51
     */
52
    private static array $extMethods = [];
53

54
    protected mixed $value = null;
55

56
    protected Html $control;
57

58
    protected Html $label;
59

60
    /**
61
     * @var bool|bool[]
62
     */
63
    protected bool|array $disabled = false;
64

65
    private string|Stringable|null $caption;
66

67
    /**
68
     * @var array<string|Stringable>
69
     */
70
    private array $errors = [];
71

72
    private ?bool $omitted = null;
73

74
    private Rules $rules;
75

76
    /**
77
     * true means autodetect
78
     *
79
     * @var Translator|true|null
80
     */
81
    private Translator|bool|null $translator = true;
82

83
    /**
84
     * @var array<mixed>
85
     */
86
    private array $options = [];
87

88
    public function __construct(string|Stringable|null $caption = null)
1✔
89
    {
90
        $this->control = Html::el('input', ['type' => null, 'name' => null]);
1✔
91
        $this->label = Html::el('label');
1✔
92
        $this->caption = $caption;
1✔
93
        $this->rules = new Rules($this);
1✔
94
        $this->setValue(null);
1✔
95
        $this->monitor(Form::class, function (Form $form): void {
1✔
96
            if (! $this->isDisabled() && $form->isAnchored() && $form->isSubmitted() !== false) {
1✔
97
                $this->loadHttpData();
98
            }
99
        });
1✔
100
    }
1✔
101

102
    /**
103
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
104
     * @param callable $callback
105
     */
106
    public static function extensionMethod(string $name, /*callable*/ $callback): void
107
    {
NEW
108
        if (str_contains($name, '::')) { // back compatibility
×
NEW
109
            [, $name] = explode('::', $name);
×
110
        }
111

NEW
112
        self::$extMethods[$name][static::class] = $callback;
×
113
    }
114

115
    /**
116
     * Sets textual caption or label.
117
     */
118
    public function setCaption(string|Stringable $caption): static
119
    {
120
        $this->caption = $caption;
×
121
        return $this;
×
122
    }
123

124
    public function getCaption(): string|Stringable|null
125
    {
126
        return $this->caption;
1✔
127
    }
128

129
    /**
130
     * Returns form.
131
     *
132
     * @return ($throw is true ? Form : ?Form)
133
     */
134
    public function getForm(bool $throw = true): ?Form
1✔
135
    {
136
        // @phpstan-ignore return.type
137
        return $this->lookup(Form::class, $throw);
1✔
138
    }
139

140
    /**
141
     * Loads HTTP data.
142
     */
143
    public function loadHttpData(): void
144
    {
NEW
145
        $this->setValue($this->getHttpData(Form::DataText));
×
146
    }
147

148
    /**
149
     * Loads HTTP data.
150
     *
151
     * @param Form::Data* $type
152
     */
153
    protected function getHttpData(int $type, ?string $htmlTail = null): mixed
154
    {
NEW
155
        return $this->getForm()->getHttpData($type, $this->getHtmlName() . ($htmlTail ?? ''));
×
156
    }
157

158
    /**
159
     * Returns HTML name of control.
160
     */
161
    public function getHtmlName(): string
162
    {
163
        return $this->control->name ?? Nette\Forms\Helpers::generateHtmlName($this->lookupPath(Form::class));
×
164
    }
165

166
    /********************* interface Control ****************d*g**/
167

168
    /**
169
     * Sets control's value.
170
     *
171
     * @internal
172
     */
173
    public function setValue(mixed $value): static
174
    {
175
        $this->value = $value;
×
176
        return $this;
×
177
    }
178

179
    /**
180
     * Returns control's value.
181
     */
182
    public function getValue(): mixed
183
    {
184
        return $this->value;
×
185
    }
186

187
    /**
188
     * Is control filled?
189
     */
190
    public function isFilled(): bool
191
    {
192
        $value = $this->getValue();
×
193
        return $value !== null && $value !== [] && $value !== '';
×
194
    }
195

196
    /**
197
     * Sets control's default value.
198
     *
199
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint
200
     * @return static
201
     * @phpstan-ignore missingType.parameter
202
     */
203
    public function setDefaultValue($value): static
204
    {
205
        $form = $this->getForm(throw: false);
×
NEW
206
        if ($this->isDisabled() || $form === null || ! $form->isAnchored() || $form->isSubmitted() === false) {
×
207
            $this->setValue($value);
×
208
        }
209
        return $this;
×
210
    }
211

212
    /**
213
     * Disables or enables control.
214
     *
215
     * @return static
216
     */
217
    public function setDisabled(bool $state = true): static
218
    {
219
        $this->disabled = $state;
×
220
        if ($state) {
×
221
            $this->setValue(null);
×
222
        } else {
NEW
223
            $form = $this->getForm(throw: false);
×
NEW
224
            if ($form !== null && $form->isAnchored() && $form->isSubmitted() !== false) {
×
NEW
225
                $this->loadHttpData();
×
226
            }
227
        }
228
        return $this;
×
229
    }
230

231
    /**
232
     * Is control disabled?
233
     */
234
    public function isDisabled(): bool
235
    {
236
        return $this->disabled === true;
×
237
    }
238

239
    /**
240
     * Sets whether control value is excluded from $form->getValues() result.
241
     */
242
    public function setOmitted(bool $state = true): static
243
    {
244
        $this->omitted = $state;
×
245
        return $this;
×
246
    }
247

248
    /**
249
     * Is control value excluded from $form->getValues() result?
250
     */
251
    public function isOmitted(): bool
252
    {
253
        return $this->omitted || ($this->isDisabled() && $this->omitted === null);
×
254
    }
255

256
    /********************* rendering ****************d*g**/
257

258
    /**
259
     * Generates control's HTML element.
260
     */
261
    public function getControl(): ?Html
262
    {
263
        $this->setOption('rendered', true);
×
264
        $el = clone $this->control;
×
NEW
265
        $rules = Nette\Forms\Helpers::exportRules($this->rules);
×
266
        return $el->addAttributes([
×
267
            'name' => $this->getHtmlName(),
×
268
            'id' => $this->getHtmlId(),
×
269
            'required' => $this->isRequired(),
×
270
            'disabled' => $this->isDisabled(),
×
NEW
271
            'data-nette-rules' => $rules !== [] ? $rules : null,
×
272
        ]);
273
    }
274

275
    /**
276
     * Generates label's HTML element.
277
     */
278
    public function getLabel(string|Stringable|null $caption = null): ?Html
279
    {
280
        $label = clone $this->label;
×
281
        $label->for = $this->getHtmlId();
×
282
        $caption ??= $this->caption;
×
283
        $translator = $this->getForm()->getTranslator();
×
NEW
284
        $label->setText($translator !== null && $caption !== null && ! $caption instanceof Nette\HtmlStringable ? $translator->translate($caption) : $caption);
×
285
        return $label;
×
286
    }
287

288
    public function getControlPart(): ?Html
289
    {
290
        return $this->getControl();
×
291
    }
292

293
    public function getLabelPart(): ?Html
294
    {
295
        return $this->getLabel();
×
296
    }
297

298
    /**
299
     * Returns control's HTML element template.
300
     */
301
    public function getControlPrototype(): Html
302
    {
303
        return $this->control;
×
304
    }
305

306
    /**
307
     * Returns label's HTML element template.
308
     */
309
    public function getLabelPrototype(): Html
310
    {
311
        return $this->label;
×
312
    }
313

314
    /**
315
     * Changes control's HTML id.
316
     */
317
    public function setHtmlId(string|bool|null $id): static
318
    {
319
        $this->control->id = $id;
×
320
        return $this;
×
321
    }
322

323
    /**
324
     * Returns control's HTML id.
325
     */
326
    public function getHtmlId(): string|bool|null
327
    {
328
        if (! isset($this->control->id)) {
1✔
329
            $form = $this->getForm();
1✔
330
            $prefix = $form instanceof Nette\Application\UI\Form || $form->getName() === null
1✔
331
                ? ''
1✔
332
                : $form->getName() . '-';
×
333
            $this->control->id = sprintf(self::$idMask, $prefix . $this->lookupPath());
1✔
334
        }
335
        return $this->control->id;
1✔
336
    }
337

338
    /**
339
     * Changes control's HTML attribute.
340
     */
341
    public function setHtmlAttribute(string $name, mixed $value = true): static
342
    {
343
        // @phpstan-ignore property.dynamicName
344
        $this->control->$name = $value;
×
NEW
345
        if ($name === 'name') {
×
NEW
346
            $form = $this->getForm(false);
×
347
            if (
NEW
348
                $form !== null
×
NEW
349
                && ! $this->isDisabled()
×
NEW
350
                && $form->isAnchored()
×
NEW
351
                && $form->isSubmitted() !== false
×
352
            ) {
NEW
353
                $this->loadHttpData();
×
354
            }
355
        }
356

357
        return $this;
×
358
    }
359

360
    /**
361
     * @deprecated  use setHtmlAttribute()
362
     */
363
    public function setAttribute(string $name, mixed $value = true): static
364
    {
365
        return $this->setHtmlAttribute($name, $value);
×
366
    }
367

368
    /********************* translator ****************d*g**/
369

370
    /**
371
     * Sets translate adapter.
372
     */
373
    public function setTranslator(?Translator $translator): static
374
    {
375
        $this->translator = $translator;
×
376
        return $this;
×
377
    }
378

379
    /**
380
     * Returns translate adapter.
381
     */
382
    public function getTranslator(): ?Translator
383
    {
384
        if ($this->translator === true) {
1✔
385
            return $this->getForm(false)?->getTranslator();
1✔
386
        }
387
        return $this->translator;
×
388
    }
389

390
    /**
391
     * Returns translated string.
392
     */
393
    public function translate(mixed $value, mixed ...$parameters): mixed
1✔
394
    {
395
        $translator = $this->getTranslator();
1✔
396
        if ($translator !== null) {
1✔
397
            $tmp = is_array($value) ? [&$value] : [[&$value]];
1✔
398
            foreach ($tmp[0] as &$v) {
1✔
399
                // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
400
                if ($v != null && ! $v instanceof Nette\HtmlStringable) { // intentionally ==
1✔
401
                    $v = $translator->translate($v, ...$parameters);
1✔
402
                }
403
            }
404
        }
405
        return $value;
1✔
406
    }
407

408
    /********************* rules ****************d*g**/
409

410
    /**
411
     * Adds a validation rule.
412
     *
413
     * @return static
414
     */
415
    public function addRule(
416
        callable|string $validator,
417
        string|Stringable|null $errorMessage = null,
418
        mixed $arg = null,
419
    ): static
420
    {
421
        $this->rules->addRule($validator, $errorMessage, $arg);
×
422
        return $this;
×
423
    }
424

425
    /**
426
     * Adds a validation condition a returns new branch.
427
     *
428
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
429
     * @param callable|string $validator
430
     */
431
    public function addCondition($validator, mixed $value = null): Rules
432
    {
433
        return $this->rules->addCondition($validator, $value);
×
434
    }
435

436
    /**
437
     * Adds a validation condition based on another control a returns new branch.
438
     *
439
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
440
     * @param callable|string $validator
441
     */
442
    public function addConditionOn(Control $control, $validator, mixed $value = null): Rules
443
    {
444
        return $this->rules->addConditionOn($control, $validator, $value);
×
445
    }
446

447
    /**
448
     * Adds an input filter callback.
449
     */
450
    public function addFilter(callable $filter): static
451
    {
452
        $this->getRules()->addFilter($filter);
×
453
        return $this;
×
454
    }
455

456
    public function getRules(): Rules
457
    {
458
        return $this->rules;
×
459
    }
460

461
    /**
462
     * Makes control mandatory.
463
     */
464
    public function setRequired(string|Stringable|bool $value = true): static
465
    {
466
        $this->rules->setRequired($value);
×
467
        return $this;
×
468
    }
469

470
    /**
471
     * Is control mandatory?
472
     */
473
    public function isRequired(): bool
474
    {
475
        return $this->rules->isRequired();
×
476
    }
477

478
    /**
479
     * Performs the server side validation.
480
     */
481
    public function validate(): void
482
    {
483
        if ($this->isDisabled()) {
×
484
            return;
×
485
        }
486
        $this->cleanErrors();
×
487
        $this->rules->validate();
×
488
    }
489

490
    /**
491
     * Adds error message to the list.
492
     */
493
    public function addError(string|Stringable $message, bool $translate = true): void
1✔
494
    {
495
        $this->errors[] = $translate ? $this->translate($message) : $message;
1✔
496
    }
1✔
497

498
    /**
499
     * Returns errors corresponding to control.
500
     */
501
    public function getError(): ?string
502
    {
503
        return $this->errors !== [] ? implode(' ', array_unique($this->errors)) : null;
1✔
504
    }
505

506
    /**
507
     * Returns errors corresponding to control.
508
     *
509
     * @return array<string|Stringable>
510
     */
511
    public function getErrors(): array
512
    {
513
        return array_unique($this->errors);
1✔
514
    }
515

516
    public function hasErrors(): bool
517
    {
518
        return (bool) $this->errors;
1✔
519
    }
520

521
    public function cleanErrors(): void
522
    {
523
        $this->errors = [];
1✔
524
    }
1✔
525

526
    /********************* user data ****************d*g**/
527

528
    /**
529
     * Sets user-specific option.
530
     *
531
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
532
     * @param string|int $key
533
     */
534
    public function setOption($key, mixed $value): static
1✔
535
    {
536
        if ($value === null) {
1✔
537
            unset($this->options[$key]);
×
538
        } else {
539
            $this->options[$key] = $value;
1✔
540
        }
541
        return $this;
1✔
542
    }
543

544
    /**
545
     * Returns user-specific option.
546
     *
547
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
548
     * @param string|int $key
549
     */
550
    public function getOption($key): mixed
551
    {
552
        if (func_num_args() > 1) {
×
553
            trigger_error(__METHOD__ . '() parameter $default is deprecated, use operator ??', E_USER_DEPRECATED);
×
554
            $default = func_get_arg(1);
×
555
        }
556
        return $this->options[$key] ?? $default ?? null;
×
557
    }
558

559
    /**
560
     * Returns user-specific options.
561
     *
562
     * @return array<mixed>
563
     */
564
    public function getOptions(): array
565
    {
566
        return $this->options;
×
567
    }
568

569
    /********************* extension methods ****************d*g**/
570

571
    /**
572
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint
573
     * @param array<mixed> $args
574
     * @phpstan-ignore shipmonk.missingNativeReturnTypehint
575
     */
576
    public function __call(string $name, array $args)
577
    {
578
        $class = static::class;
×
579
        do {
580
            if (isset(self::$extMethods[$name][$class])) {
×
581
                return (self::$extMethods[$name][$class])($this, ...$args);
×
582
            }
583
            $class = get_parent_class($class);
×
584
        } while ($class);
×
585
        return parent::__call($name, $args);
×
586
    }
587

588
}
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

© 2025 Coveralls, Inc