Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
160 / 160
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
ValidationCollector
100.00% covered (success)
100.00%
160 / 160
100.00% covered (success)
100.00%
9 / 9
35
100.00% covered (success)
100.00%
1 / 1
 setValidation
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getActivities
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 setErrorInDebugData
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 getContents
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 renderValidations
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
6
 renderRuleset
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
1 / 1
4
 renderValidatorsRules
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
3
 getValidatorsRules
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
1 / 1
11
 sRule
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Validation Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Validation\Debug;
11
12use Framework\Debug\Collector;
13use Framework\Validation\Validation;
14use ReflectionMethod;
15
16/**
17 * Class ValidationCollector.
18 *
19 * @package validation
20 */
21class ValidationCollector extends Collector
22{
23    protected Validation $validation;
24    /**
25     * @var array<string,array<string,mixed>>
26     */
27    protected array $validatorsRules;
28
29    public function setValidation(Validation $validation) : static
30    {
31        $this->validation = $validation;
32        return $this;
33    }
34
35    public function getActivities() : array
36    {
37        $activities = [];
38        foreach ($this->getData() as $index => $data) {
39            $activities[] = [
40                'collector' => $this->getName(),
41                'class' => static::class,
42                'description' => 'Run validation ' . ($index + 1),
43                'start' => $data['start'],
44                'end' => $data['end'],
45            ];
46        }
47        return $activities;
48    }
49
50    public function setErrorInDebugData(string $field, string $error, int $index = -1) : static
51    {
52        $data = $this->getData();
53        if ($index === -1) {
54            $index = \array_key_last($data);
55            if ($index === null) {
56                return $this;
57            }
58        }
59        $data[$index]['errors'][$field] = $error;
60        $this->data = $data;
61        return $this;
62    }
63
64    public function getContents() : string
65    {
66        if ( ! isset($this->validation)) {
67            return '<p>A Validation instance has not been set in this collector.</p>';
68        }
69        \ob_start(); ?>
70        <?= $this->renderValidations() ?>
71        <?php $this->validatorsRules = $this->getValidatorsRules(); ?>
72        <h1>Ruleset</h1>
73        <?= $this->renderRuleset() ?>
74        <h1>Validators Rules</h1>
75        <?php
76        echo $this->renderValidatorsRules();
77        return \ob_get_clean(); // @phpstan-ignore-line
78    }
79
80    protected function renderValidations() : string
81    {
82        if ( ! $this->hasData()) {
83            return '<p>Validation did not run.</p>';
84        }
85        $count = \count($this->getData());
86        \ob_start(); ?>
87        <p>Validation ran <?= $count ?> time<?= $count === 1 ? '' : 's' ?>.
88        <p>
89        <table>
90            <thead>
91            <tr>
92                <th>#</th>
93                <th>Type</th>
94                <th>Errors Count</th>
95                <th>Error Field</th>
96                <th>Error Message</th>
97                <th title="Seconds">Time</th>
98            </tr>
99            </thead>
100            <tbody>
101            <?php
102            foreach ($this->getData() as $index => $item):
103                $count = \count($item['errors']);
104                $errors = [];
105                foreach ($item['errors'] as $field => $error) :
106                    $errors[] = [
107                        'field' => $field,
108                        'error' => $error,
109                    ];
110                endforeach; ?>
111                <tr>
112                    <td rowspan="<?= $count ?>"><?= $index + 1 ?></td>
113                    <td rowspan="<?= $count ?>"><?= $item['type'] ?></td>
114                    <td rowspan="<?= $count ?>"><?= $count ?></td>
115                    <td><?= $errors[0]['field'] ?? '' ?></td>
116                    <td><?= $errors[0]['error'] ?? '' ?></td>
117                    <td rowspan="<?= $count ?>"><?= \round($item['end'] - $item['start'], 6) ?></td>
118                </tr>
119                <?php for ($i = 1; $i < $count; $i++): ?>
120                    <tr>
121                        <td><?= $errors[$i]['field'] ?></td>
122                        <td><?= $errors[$i]['error'] ?></td>
123                    </tr>
124                <?php endfor;
125            endforeach; ?>
126            </tbody>
127        </table>
128        <?php return \ob_get_clean(); // @phpstan-ignore-line
129    }
130
131    protected function renderRuleset() : string
132    {
133        if ( ! $this->validation->getRules()) {
134            return '<p>No rules have been set.</p>';
135        }
136        \ob_start(); ?>
137        <p>The following rules have been set:</p>
138        <table>
139            <thead>
140            <tr>
141                <th>#</th>
142                <th>Field</th>
143                <th>Label</th>
144                <th>Rule</th>
145                <th>Possible Error Message</th>
146            </tr>
147            </thead>
148            <tbody>
149            <?php foreach ($this->validation->getRuleset() as $index => $set): ?>
150                <?php $count = \count($set['rules']); ?>
151                <tr>
152                    <td rowspan="<?= $count ?>"><?= $index + 1 ?></td>
153                    <td rowspan="<?= $count ?>"><?= \htmlentities($set['field']) ?></td>
154                    <td rowspan="<?= $count ?>"><?= \htmlentities((string) $set['label']) ?></td>
155                    <td><?= $this->sRule($set['rules'][0]['rule'], $this->validatorsRules) ?></td>
156                    <td><?= \htmlentities($set['rules'][0]['message']) ?></td>
157                </tr>
158                <?php for ($i = 1; $i < $count; $i++): ?>
159                    <tr>
160                        <td><?= $this->sRule($set['rules'][$i]['rule'], $this->validatorsRules) ?></td>
161                        <td><?= \htmlentities($set['rules'][$i]['message']) ?></td>
162                    </tr>
163                <?php endfor ?>
164            <?php endforeach ?>
165            </tbody>
166        </table>
167        <?php
168        return \ob_get_clean(); // @phpstan-ignore-line
169    }
170
171    protected function renderValidatorsRules() : string
172    {
173        \ob_start() ?>
174        <p>There are <?= \count($this->validatorsRules) ?> rules available:</p>
175        <table>
176            <thead>
177            <tr>
178                <th>Rule</th>
179                <th>Params</th>
180                <th>Message Pattern</th>
181                <th>Validator</th>
182            </tr>
183            </thead>
184            <tbody>
185            <?php foreach ($this->validatorsRules as $rule => $data) : ?>
186                <tr>
187                    <td><?= \htmlentities($rule) ?></td>
188                    <td>
189                        <?php if ($data['params']): ?>
190                            <pre><code class="language-php"><?= \htmlentities($data['params']) ?></code></pre>
191                        <?php endif ?>
192                    </td>
193                    <td>
194                        <pre><code class="language-icu-message-format"><?=
195                                \htmlentities(
196                                    $this->validation->getLanguage()->render('validation', $rule)
197                                ) ?></code></pre>
198                    </td>
199                    <td><?= \htmlentities($data['validator']) ?></td>
200                </tr>
201            <?php endforeach ?>
202            </tbody>
203        </table>
204        <?php
205        return \ob_get_clean(); // @phpstan-ignore-line
206    }
207
208    /**
209     * @return array<string,array<string,mixed>>
210     */
211    protected function getValidatorsRules() : array
212    {
213        $rules = [];
214        foreach (\array_reverse($this->validation->getValidators()) as $validator) {
215            foreach (\get_class_methods($validator) as $method) {
216                $method = (string) $method;
217                if (\is_callable([$validator, $method])) {
218                    $params = [];
219                    $reflection = new ReflectionMethod($validator, $method);
220                    foreach ($reflection->getParameters() as $parameter) {
221                        $name = $parameter->getName();
222                        if (\in_array($name, ['field', 'data'], true)) {
223                            continue;
224                        }
225                        $value = '';
226                        if ($parameter->isDefaultValueAvailable()) {
227                            $value = $parameter->getDefaultValue();
228                            $type = \get_debug_type($value);
229                            if ($type === 'string') {
230                                $value = "'" . \strtr($value, [
231                                        "'" => "\\'",
232                                    ]) . "'";
233                            } elseif ($type === 'null') {
234                                $value = 'null';
235                            }
236                            $value = ' = ' . $value;
237                        }
238                        $name = '$' . $name;
239                        if ($parameter->isVariadic()) {
240                            $name = '...' . $name;
241                        }
242                        $params[] = $name . $value;
243                    }
244                    $rules[$method] = [
245                        'validator' => \is_string($validator) ? $validator : $validator::class,
246                        'params' => \implode(', ', $params),
247                    ];
248                }
249            }
250        }
251        $rules['optional'] = [
252            'validator' => '',
253            'params' => '',
254        ];
255        $rules['blank'] = [
256            'validator' => '',
257            'params' => '',
258        ];
259        $rules['null'] = [
260            'validator' => '',
261            'params' => '',
262        ];
263        $rules['empty'] = [
264            'validator' => '',
265            'params' => '',
266        ];
267        \ksort($rules);
268        return $rules;
269    }
270
271    /**
272     * @param string $rule
273     * @param array<string,mixed> $validatorsRules
274     *
275     * @return string
276     */
277    protected function sRule(string $rule, array $validatorsRules) : string
278    {
279        if ($rule === 'optional'
280            || \array_key_exists(\explode(':', $rule)[0], $validatorsRules)
281        ) {
282            return \htmlentities($rule);
283        }
284        return '<s title="Rule not available">' . \htmlentities($rule) . '</s>';
285    }
286}