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

JBZoo / SimpleTypes / 8108522739

28 Jan 2024 12:22PM UTC coverage: 99.341%. Remained the same
8108522739

push

github

web-flow
Update PHP version to 8.3 and bump jbzoo dependencies to ^7.1 (#14)

1 of 1 new or added line in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

754 of 759 relevant lines covered (99.34%)

72.74 hits per line

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

99.24
/src/Type/AbstractType.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - SimpleTypes.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/SimpleTypes
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\SimpleTypes\Type;
18

19
use JBZoo\SimpleTypes\Config\AbstractConfig;
20
use JBZoo\SimpleTypes\Exception;
21
use JBZoo\SimpleTypes\Formatter;
22
use JBZoo\SimpleTypes\Parser;
23
use JBZoo\Utils\Str;
24

25
use function JBZoo\Utils\isStrEmpty;
26

27
/**
28
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
29
 * @SuppressWarnings(PHPMD.TooManyMethods)
30
 * @SuppressWarnings(PHPMD.ExcessivePublicCount)
31
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
32
 *
33
 * @property string $value
34
 * @property string $rule
35
 */
36
abstract class AbstractType
37
{
38
    protected static int $counter = 0;
39

40
    protected int       $uniqueId      = 0;
41
    protected string    $type          = '';
42
    protected float|int $internalValue = 0;
43
    protected string    $internalRule  = '';
44
    protected string    $default       = '';
45
    protected array     $logs          = [];
46
    protected bool      $isDebug       = false;
47
    protected Parser    $parser;
48
    protected Formatter $formatter;
49

50
    public function __construct(null|array|float|int|string $value = null, ?AbstractConfig $config = null)
51
    {
52
        $this->prepareObject($value, $config);
445✔
53
    }
54

55
    public function __toString()
56
    {
57
        $this->log('__toString() called');
5✔
58

59
        return $this->text();
5✔
60
    }
61

62
    /**
63
     * Serialize.
64
     * @return array
65
     */
66
    public function __sleep()
67
    {
68
        $result   = [];
5✔
69
        $reflect  = new \ReflectionClass($this);
5✔
70
        $propList = $reflect->getProperties();
5✔
71

72
        foreach ($propList as $prop) {
5✔
73
            if ($prop->isStatic()) {
5✔
74
                continue;
5✔
75
            }
76
            $result[] = $prop->name;
5✔
77
        }
78

79
        $this->log('Serialized');
5✔
80

81
        return $result;
5✔
82
    }
83

84
    /**
85
     * Wake up after serialize.
86
     */
87
    public function __wakeup(): void
88
    {
89
        $this->log('--> wakeup start');
5✔
90
        $this->prepareObject($this->dump(false));
5✔
91
        $this->log('<-- Wakeup finish');
5✔
92
    }
93

94
    /**
95
     * Clone object.
96
     */
97
    public function __clone()
98
    {
99
        self::$counter++;
20✔
100
        $recentId       = $this->uniqueId;
20✔
101
        $this->uniqueId = self::$counter;
20✔
102

103
        $this->log(
20✔
104
            "Cloned from id='{$recentId}' and created new with id='{$this->uniqueId}'; dump=" . $this->dump(false),
20✔
105
        );
20✔
106
    }
107

108
    /**
109
     * @return float|string
110
     */
111
    public function __get(string $name)
112
    {
113
        $name = \strtolower($name);
10✔
114

115
        if ($name === 'value') {
10✔
116
            return $this->val();
5✔
117
        }
118

119
        if ($name === 'rule') {
10✔
120
            return $this->getRule();
5✔
121
        }
122

123
        throw new Exception("{$this->type}: Undefined __get() called: '{$name}'");
5✔
124
    }
125

126
    /**
127
     * @noinspection MagicMethodsValidityInspection
128
     */
129
    public function __set(string $name, mixed $value): void
130
    {
131
        if ($name === 'value') {
10✔
132
            $this->set([$value]);
5✔
133
        } elseif ($name === 'rule') {
10✔
134
            $this->convert($value);
5✔
135
        } else {
136
            throw new Exception("{$this->type}: Undefined __set() called: '{$name}' = '{$value}'");
5✔
137
        }
138
    }
139

140
    /**
141
     * Experimental! Methods aliases.
142
     * @deprecated
143
     */
144
    public function __call(string $name, array $arguments): mixed
145
    {
146
        $name = \strtolower($name);
20✔
147
        if ($name === 'value') {
20✔
148
            return \call_user_func_array([$this, 'val'], $arguments);
5✔
149
        }
150

151
        if ($name === 'plus') {
15✔
152
            return \call_user_func_array([$this, 'add'], $arguments);
5✔
153
        }
154

155
        if ($name === 'minus') {
10✔
156
            return \call_user_func_array([$this, 'subtract'], $arguments);
5✔
157
        }
158

159
        throw new Exception("{$this->type}: Called undefined method: '{$name}'");
5✔
160
    }
161

162
    public function __invoke(): self
163
    {
164
        $args         = \func_get_args();
15✔
165
        $argsCount    = \count($args);
15✔
166
        $shortArgList = 1;
15✔
167
        $fullArgList  = 2;
15✔
168

169
        if ($argsCount === 0) {
15✔
170
            $this->error('Undefined arguments');
5✔
171
        } elseif ($argsCount === $shortArgList) {
10✔
172
            $rules = $this->formatter->getList();
5✔
173

174
            if (\array_key_exists($args[0], $rules)) {
5✔
175
                return $this->convert($args[0]);
5✔
176
            }
177

178
            return $this->set($args[0]);
5✔
179
        } elseif ($argsCount === $fullArgList) {
10✔
180
            return $this->set([$args[0], $args[1]]);
5✔
181
        }
182

183
        throw new Exception("{$this->type}: Too many arguments");
5✔
184
    }
185

186
    public function getId(): int
187
    {
188
        return $this->uniqueId;
10✔
189
    }
190

191
    public function val(?string $rule = null): float
192
    {
193
        $rule = Parser::cleanRule($rule);
165✔
194

195
        if ($rule !== $this->internalRule && !isStrEmpty($rule)) {
165✔
196
            return $this->customConvert($rule);
80✔
197
        }
198

199
        return $this->internalValue;
140✔
200
    }
201

202
    public function text(?string $rule = null): string
203
    {
204
        $rule = !isStrEmpty($rule) ? $this->parser->checkRule($rule) : $this->internalRule;
30✔
205
        $this->log("Formatted output in '{$rule}' as 'text'");
30✔
206

207
        return $this->formatter->text($this->val($rule), $rule);
30✔
208
    }
209

210
    public function noStyle(?string $rule = null): string
211
    {
212
        $rule = !isStrEmpty($rule) ? $this->parser->checkRule($rule) : $this->internalRule;
5✔
213
        $this->log("Formatted output in '{$rule}' as 'noStyle'");
5✔
214

215
        return $this->formatter->text($this->val($rule), $rule, false);
5✔
216
    }
217

218
    public function html(?string $rule = null): string
219
    {
220
        $rule = !isStrEmpty($rule) ? $this->parser->checkRule($rule) : $this->internalRule;
5✔
221
        $this->log("Formatted output in '{$rule}' as 'html'");
5✔
222

223
        return $this->formatter->html(
5✔
224
            ['value' => $this->val($rule), 'rule' => $rule],
5✔
225
            ['value' => $this->internalValue, 'rule' => $this->internalRule],
5✔
226
            ['id'    => $this->uniqueId],
5✔
227
        );
5✔
228
    }
229

230
    public function htmlInput(?string $rule = null, ?string $name = null, bool $formatted = false): string
231
    {
232
        $rule = !isStrEmpty($rule) ? $this->parser->checkRule($rule) : $this->internalRule;
5✔
233
        $this->log("Formatted output in '{$rule}' as 'input'");
5✔
234

235
        return $this->formatter->htmlInput(
5✔
236
            ['value' => $this->val($rule), 'rule' => $rule],
5✔
237
            ['value' => $this->internalValue, 'rule' => $this->internalRule],
5✔
238
            ['id'    => $this->uniqueId, 'name' => $name, 'formatted' => $formatted],
5✔
239
        );
5✔
240
    }
241

242
    public function isRule(string $rule): bool
243
    {
244
        $rule = $this->parser->checkRule($rule);
20✔
245

246
        return $rule === $this->internalRule;
20✔
247
    }
248

249
    public function getRule(): string
250
    {
251
        return $this->internalRule;
75✔
252
    }
253

254
    public function isEmpty(): bool
255
    {
256
        return (float)$this->internalValue === 0.0;
10✔
257
    }
258

259
    public function isPositive(): bool
260
    {
261
        return $this->internalValue > 0;
5✔
262
    }
263

264
    public function isNegative(): bool
265
    {
266
        return $this->internalValue < 0;
5✔
267
    }
268

269
    public function getRules(): array
270
    {
271
        return $this->formatter->getList();
5✔
272
    }
273

274
    public function data(bool $toString = false): array|string
275
    {
276
        $data = [(string)$this->val(), $this->getRule()];
5✔
277

278
        return $toString ? \implode(' ', $data) : $data;
5✔
279
    }
280

281
    public function getClone(): self
282
    {
283
        return clone $this;
10✔
284
    }
285

286
    /**
287
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
288
     */
289
    public function compare(
290
        null|array|float|int|self|string $value,
291
        string $mode = '==',
292
        int $round = Formatter::ROUND_DEFAULT,
293
    ): bool {
294
        // prepare value
295
        $value = $this->getValidValue($value);
35✔
296

297
        $mode = \trim($mode);
35✔
298
        $mode = \in_array($mode, ['=', '==', '==='], true) ? '==' : $mode;
35✔
299

300
        $val1 = \round($this->val($this->internalRule), $round);
35✔
301
        $val2 = \round($value->val($this->internalRule), $round);
35✔
302

303
        $this->log(
35✔
304
            "Compared '{$this->dump(false)}' {$mode} '{$value->dump(false)}' // {$val1} {$mode} {$val2}, r={$round}",
35✔
305
        );
35✔
306

307
        if ($mode === '==') {
35✔
308
            return $val1 === $val2;
30✔
309
        }
310

311
        if ($mode === '!=' || $mode === '!==') {
20✔
312
            return $val1 !== $val2;
10✔
313
        }
314

315
        if ($mode === '<') {
20✔
316
            return $val1 < $val2;
15✔
317
        }
318

319
        if ($mode === '>') {
20✔
320
            return $val1 > $val2;
15✔
321
        }
322

323
        if ($mode === '<=') {
20✔
324
            return $val1 <= $val2;
15✔
325
        }
326

327
        if ($mode === '>=') {
20✔
328
            return $val1 >= $val2;
15✔
329
        }
330

331
        throw new Exception("{$this->type}: Undefined compare mode: {$mode}");
5✔
332
    }
333

334
    public function setEmpty(bool $getClone = false): self
335
    {
336
        return $this->modifier(0.0, 'Set empty', $getClone);
5✔
337
    }
338

339
    public function add(null|array|float|int|self|string $value, bool $getClone = false): self
340
    {
341
        return $this->customAdd($value, $getClone);
45✔
342
    }
343

344
    public function subtract(null|array|float|int|self|string $value, bool $getClone = false): self
345
    {
346
        return $this->customAdd($value, $getClone, true);
25✔
347
    }
348

349
    public function convert(string $newRule, bool $getClone = false): self
350
    {
351
        if ($newRule === '') {
130✔
352
            $newRule = $this->internalRule;
5✔
353
        }
354

355
        $newRule = $this->parser->checkRule($newRule);
130✔
356

357
        $obj = $getClone ? clone $this : $this;
120✔
358

359
        if ($newRule !== $obj->internalRule) {
120✔
360
            $obj->internalValue = $obj->customConvert($newRule, true);
115✔
361
            $obj->internalRule  = $newRule;
115✔
362
        }
363

364
        return $obj;
120✔
365
    }
366

367
    public function invert(bool $getClone = false): self
368
    {
369
        $logMess = 'Invert sign';
5✔
370
        if ($this->internalValue > 0) {
5✔
371
            $newValue = -1 * $this->internalValue;
5✔
372
        } elseif ($this->internalValue < 0) {
5✔
373
            $newValue = \abs((float)$this->internalValue);
5✔
374
        } else {
375
            $newValue = $this->internalValue;
5✔
376
        }
377

378
        return $this->modifier($newValue, $logMess, $getClone);
5✔
379
    }
380

381
    public function positive(bool $getClone = false): self
382
    {
383
        return $this->modifier(\abs((float)$this->internalValue), 'Set positive/abs', $getClone);
10✔
384
    }
385

386
    public function negative(bool $getClone = false): self
387
    {
388
        return $this->modifier(-1 * \abs((float)$this->internalValue), 'Set negative', $getClone);
10✔
389
    }
390

391
    public function abs(bool $getClone = false): self
392
    {
393
        return $this->positive($getClone);
5✔
394
    }
395

396
    public function multiply(float $number, bool $getClone = false): self
397
    {
398
        $multiplier = Parser::cleanValue($number);
15✔
399
        $newValue   = $multiplier * $this->internalValue;
15✔
400

401
        return $this->modifier($newValue, "Multiply with '{$multiplier}'", $getClone);
15✔
402
    }
403

404
    public function division(float $number, bool $getClone = false): self
405
    {
406
        $divider = Parser::cleanValue($number);
5✔
407

408
        return $this->modifier($this->internalValue / $divider, "Division with '{$divider}'", $getClone);
5✔
409
    }
410

411
    public function percent(self|string $value, bool $revert = false): self
412
    {
413
        $value = $this->getValidValue($value);
5✔
414

415
        $percent = 0.0;
5✔
416
        if (!$this->isEmpty() && !$value->isEmpty()) {
5✔
417
            $percent = ($this->internalValue / $value->val($this->internalRule)) * 100;
5✔
418
        }
419

420
        if ($revert) {
5✔
421
            $percent = 100 - $percent;
5✔
422
        }
423

424
        $result = $this->getValidValue("{$percent}%");
5✔
425
        $this->log("Calculate percent; '{$this->dump(false)}' / {$value->dump(false)} = {$result->dump(false)}");
5✔
426

427
        return $result;
5✔
428
    }
429

430
    public function customFunc(\Closure $function, bool $getClone = false): self
431
    {
432
        $this->log('--> Function start');
5✔
433
        $function($this);
5✔
434

435
        return $this->modifier($this->internalValue, '<-- Function finished', $getClone);
5✔
436
    }
437

438
    public function set(null|array|float|int|self|string $value, bool $getClone = false): self
439
    {
440
        $value = $this->getValidValue($value);
15✔
441

442
        $this->internalValue = $value->val();
15✔
443
        $this->internalRule  = $value->getRule();
15✔
444

445
        return $this->modifier($this->internalValue, "Set new value = '{$this->dump(false)}'", $getClone);
15✔
446
    }
447

448
    public function round(int $roundValue, string $mode = Formatter::ROUND_CLASSIC): self
449
    {
450
        $oldValue = $this->internalValue;
25✔
451
        $newValue = $this->formatter->round($this->internalValue, $this->internalRule, [
25✔
452
            'roundValue' => $roundValue,
25✔
453
            'roundType'  => $mode,
25✔
454
        ]);
25✔
455

456
        $this->log("Rounded (size={$roundValue}; type={$mode}) '{$oldValue}' => {$newValue}");
20✔
457

458
        $this->internalValue = $newValue;
20✔
459

460
        return $this;
20✔
461
    }
462

463
    public function getValidValue(null|array|float|int|self|string $value): self
464
    {
465
        if ($value instanceof self) {
110✔
466
            $thisClass = \strtolower(static::class);
35✔
467
            $valClass  = \strtolower($value::class);
35✔
468
            if ($thisClass !== $valClass) {
35✔
469
                throw new Exception("{$this->type}: No valid object type given: {$valClass}");
17✔
470
            }
471
        } else {
472
            /**
473
             * @psalm-suppress UnsafeInstantiation
474
             * @phpstan-ignore-next-line
475
             */
476
            $value = new static($value, $this->getConfig());
85✔
477
        }
478

479
        return $value;
105✔
480
    }
481

482
    public function error(string $message): void
483
    {
484
        $this->log($message);
10✔
485
        throw new Exception("{$this->type}: {$message}");
10✔
486
    }
487

488
    public function dump(bool $showId = true): string
489
    {
490
        $uniqueId = $showId ? "; id={$this->uniqueId}" : '';
440✔
491

492
        return "{$this->internalValue} {$this->internalRule}{$uniqueId}";
440✔
493
    }
494

495
    public function log(string $message): void
496
    {
497
        if ($this->isDebug) {
440✔
498
            $this->logs[] = $message;
10✔
499
        }
500
    }
501

502
    public function logs(): array
503
    {
504
        return $this->logs;
5✔
505
    }
506

507
    public function changeRule(string $rule, array $newFormat): self
508
    {
509
        $rule = Parser::cleanRule($rule);
15✔
510
        $this->formatter->changeRule($rule, $newFormat);
15✔
511
        $this->log("The rule '{$rule}' changed");
10✔
512

513
        return $this;
10✔
514
    }
515

516
    public function addRule(string $rule, array $newFormat = []): self
517
    {
518
        $form = $this->formatter;
20✔
519
        $rule = Parser::cleanRule($rule);
20✔
520
        $form->addRule($rule, $newFormat);
20✔
521
        $this->parser->addRule($rule);
10✔
522
        $this->log("The rule '{$rule}' added");
10✔
523

524
        return $this;
10✔
525
    }
526

527
    public function removeRule(string $rule): self
528
    {
529
        $rule = Parser::cleanRule($rule);
10✔
530
        $this->formatter->removeRule($rule);
10✔
531
        $this->parser->removeRule($rule);
10✔
532
        $this->log("The rule '{$rule}' removed");
10✔
533

534
        return $this;
10✔
535
    }
536

537
    public function getRuleData(string $rule): array
538
    {
539
        $rule = Parser::cleanRule($rule);
10✔
540

541
        return $this->formatter->get($rule);
10✔
542
    }
543

544
    protected function getConfig(?AbstractConfig $config = null): ?AbstractConfig
545
    {
546
        $defaultConfig = AbstractConfig::getDefault($this->type);
445✔
547

548
        $config ??= $defaultConfig;
445✔
549

550
        // Hack for getValidValue method
551
        if ($defaultConfig === null && $config !== null) {
445✔
552
            AbstractConfig::registerDefault($this->type, $config);
10✔
553
        }
554

555
        return $config;
445✔
556
    }
557

558
    /**
559
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
560
     */
561
    protected function customConvert(string $rule, bool $addToLog = false): float
562
    {
563
        $from   = $this->parser->checkRule($this->internalRule);
165✔
564
        $target = $this->parser->checkRule($rule);
165✔
565

566
        $ruleTo   = $this->formatter->get($target);
165✔
567
        $ruleFrom = $this->formatter->get($from);
165✔
568
        $ruleDef  = $this->formatter->get($this->default);
165✔
569

570
        $log = "'{$from}'=>'{$target}'";
165✔
571

572
        $result = $this->internalValue;
165✔
573
        if ($from !== $target) {
165✔
574
            if (\is_callable($ruleTo['rate']) || \is_callable($ruleFrom['rate'])) {
165✔
575
                if (\is_callable($ruleFrom['rate'])) {
45✔
576
                    $defNorm = $ruleFrom['rate']($this->internalValue, $this->default, $from);
45✔
577
                } else {
578
                    $defNorm = $this->internalValue * $ruleFrom['rate'] * $ruleDef['rate'];
25✔
579
                }
580

581
                if (\is_callable($ruleTo['rate'])) {
45✔
582
                    $result = $ruleTo['rate']($defNorm, $target, $this->default);
35✔
583
                } else {
584
                    $result = $defNorm / $ruleTo['rate'];
39✔
585
                }
586
            } else {
587
                $defNorm = $this->internalValue * $ruleFrom['rate'] * $ruleDef['rate'];
135✔
588
                $result  = $defNorm / $ruleTo['rate'];
135✔
589
            }
590

591
            if ($this->isDebug && $addToLog) {
165✔
592
                $message = [
5✔
593
                    "Converted {$log};",
5✔
594
                    "New value = {$result} {$target};",
5✔
595
                    \is_callable($ruleTo['rate']) ? "func({$from})" : "{$ruleTo['rate']} {$from}",
5✔
596
                    '=',
5✔
597
                    \is_callable($ruleFrom['rate']) ? "func({$target})" : "{$ruleFrom['rate']} {$target}",
5✔
598
                ];
5✔
599

600
                $this->log(\implode(' ', $message));
5✔
601
            }
602
        }
603

604
        return $result;
165✔
605
    }
606

607
    protected function customAdd(
608
        null|array|float|int|self|string $value,
609
        bool $getClone = false,
610
        bool $isSubtract = false,
611
    ): self {
612
        $value = $this->getValidValue($value);
55✔
613

614
        $addValue = 0;
50✔
615

616
        if ($this->internalRule === '%') {
50✔
617
            if ($value->getRule() === '%') {
10✔
618
                $addValue = $value->val();
5✔
619
            } else {
620
                $this->error("Impossible add '{$value->dump(false)}' to '{$this->dump(false)}'");
7✔
621
            }
622
        } elseif ($value->getRule() !== '%') {
45✔
623
            $addValue = $value->val($this->internalRule);
40✔
624
        } else {
625
            $addValue = $this->internalValue * $value->val() / 100;
15✔
626
        }
627

628
        if ($isSubtract) {
45✔
629
            $addValue *= -1;
25✔
630
        }
631

632
        $newValue = $this->internalValue + $addValue;
45✔
633
        $logMess  = ($isSubtract ? 'Subtract' : 'Add') . " '{$value->dump(false)}'";
45✔
634

635
        return $this->modifier($newValue, $logMess, $getClone);
45✔
636
    }
637

638
    protected function modifier(float $newValue, ?string $logMessage = null, bool $getClone = false): self
639
    {
640
        if ($getClone) {
85✔
641
            $clone = $this->getClone();
10✔
642

643
            $clone->internalValue = $newValue;
10✔
644
            $clone->log("{$logMessage}; New value = '{$clone->dump(false)}'");
10✔
645

646
            return $clone;
10✔
647
        }
648

649
        $this->internalValue = $newValue;
85✔
650
        $this->log("{$logMessage}; New value = '{$this->dump(false)}'");
85✔
651

652
        return $this;
85✔
653
    }
654

655
    private function prepareObject(null|array|float|int|string $value = null, ?AbstractConfig $config = null): void
656
    {
657
        // $this->type = Str::class \strtolower(\str_replace(__NAMESPACE__ . '\\', '', static::class));
658
        $this->type = Str::getClassName(static::class, true) ?? 'UndefinedType';
445✔
659

660
        // get custom or global config
661
        $config = $this->getConfig($config);
445✔
662
        if ($config !== null) {
445✔
663
            // debug flag (for logging)
664
            $this->isDebug = $config->isDebug;
445✔
665

666
            // set default rule
667
            $this->default = \strtolower(\trim($config->default));
445✔
668
            if (isStrEmpty($this->default)) {
445✔
UNCOV
669
                $this->error('Default rule cannot be empty!');
×
670
            }
671

672
            // create formatter helper
673
            $this->formatter = new Formatter($config->getRules(), $config->defaultParams, $this->type);
445✔
674
        } else {
UNCOV
675
            $this->formatter = new Formatter();
×
676
        }
677

678
        // check that default rule
679
        $rules = $this->formatter->getList(true);
445✔
680
        if (!\array_key_exists($this->default, $rules) || \count($rules) === 0) {
445✔
681
            throw new Exception("{$this->type}: Default rule not found!");
5✔
682
        }
683

684
        // create parser helper
685
        $this->parser = new Parser($this->default, $rules);
440✔
686

687
        // parse data
688
        [$this->internalValue, $this->internalRule] = $this->parser->parse($value);
440✔
689

690
        // count unique id
691
        self::$counter++;
440✔
692
        $this->uniqueId = self::$counter;
440✔
693

694
        // success log
695
        $this->log("Id={$this->uniqueId} has just created; dump='{$this->dump(false)}'");
440✔
696
    }
697
}
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