Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
280 / 280
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
SessionCollector
100.00% covered (success)
100.00%
280 / 280
100.00% covered (success)
100.00%
14 / 14
53
100.00% covered (success)
100.00%
1 / 1
 setSession
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setOptions
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setSaveHandler
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getContents
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
3
 renderData
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
3
 renderFlash
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 renderFlashOld
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
3
 renderFlashNew
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
3
 renderTemp
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
3
 renderSaveHandler
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
3
 renderSaveHandlerTableRows
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
5
 renderCookieParams
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
3
 renderAutoRegenerateId
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
4
 getSaveHandlerConfigs
100.00% covered (success)
100.00%
63 / 63
100.00% covered (success)
100.00%
1 / 1
17
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Session 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\Session\Debug;
11
12use Framework\Debug\Collector;
13use Framework\Debug\Debugger;
14use Framework\Helpers\ArraySimple;
15use Framework\Session\SaveHandler;
16use Framework\Session\SaveHandlers\DatabaseHandler;
17use Framework\Session\SaveHandlers\FilesHandler;
18use Framework\Session\SaveHandlers\MemcachedHandler;
19use Framework\Session\SaveHandlers\RedisHandler;
20use Framework\Session\Session;
21
22/**
23 * Class SessionCollector.
24 *
25 * @package session
26 */
27class SessionCollector extends Collector
28{
29    protected Session $session;
30    /**
31     * @var array<string>
32     */
33    protected array $options = [];
34    protected SaveHandler $saveHandler;
35
36    public function setSession(Session $session) : static
37    {
38        $this->session = $session;
39        return $this;
40    }
41
42    /**
43     * @param array<string> $options
44     *
45     * @return static
46     */
47    public function setOptions(array $options) : static
48    {
49        $this->options = $options;
50        return $this;
51    }
52
53    public function setSaveHandler(SaveHandler $handler) : static
54    {
55        $this->saveHandler = $handler;
56        return $this;
57    }
58
59    public function getContents() : string
60    {
61        if ( ! isset($this->session)) {
62            return '<p>No Session instance has been set on this collector.</p>';
63        }
64        if ( ! $this->session->isActive()) {
65            return '<p>Session is inactive.</p>';
66        }
67        \ob_start(); ?>
68        <p><strong>Name:</strong> <?= \ini_get('session.name') ?></p>
69        <p><strong>Id:</strong> <?= $this->session->id() ?></p>
70        <h1>Data</h1>
71        <?= $this->renderData() ?>
72        <h1>Flash</h1>
73        <?= $this->renderFlash() ?>
74        <h1>Temp</h1>
75        <?= $this->renderTemp() ?>
76        <h1>Save Handler</h1>
77        <?= $this->renderSaveHandler() ?>
78        <h1>Cookie Params</h1>
79        <?= $this->renderCookieParams() ?>
80        <h1>Auto Regenerate Id</h1>
81        <?php
82        echo $this->renderAutoRegenerateId();
83        return \ob_get_clean(); // @phpstan-ignore-line
84    }
85
86    protected function renderData() : string
87    {
88        $data = $this->session->getAll();
89        unset($data['$']);
90        if (empty($data)) {
91            return '<p>No data.</p>';
92        }
93        \ksort($data);
94        \ob_start(); ?>
95        <table>
96            <thead>
97            <tr>
98                <th>Key</th>
99                <th>Value</th>
100            </tr>
101            </thead>
102            <tbody>
103            <?php foreach ($data as $key => $value): ?>
104                <tr>
105                    <td><?= \htmlentities((string) $key) ?></td>
106                    <td><pre><code class="language-php"><?=
107                                \htmlentities(Debugger::makeDebugValue($value))
108                ?></code></pre>
109                    </td>
110                </tr>
111            <?php endforeach ?>
112            </tbody>
113        </table>
114        <?php
115        return \ob_get_clean(); // @phpstan-ignore-line
116    }
117
118    protected function renderFlash() : string
119    {
120        $old = $this->renderFlashOld();
121        $new = $this->renderFlashNew();
122        if ($old === '' && $new === '') {
123            return '<p>No flash data.</p>';
124        }
125        return $old . $new;
126    }
127
128    protected function renderFlashOld() : string
129    {
130        $data = ArraySimple::value('$[flash][old]', $this->session->getAll());
131        if (empty($data)) {
132            return '';
133        }
134        \ob_start(); ?>
135        <h2>Old</h2>
136        <p>Flash data available in the current request.</p>
137        <table>
138            <thead>
139            <tr>
140                <th>Key</th>
141                <th>Value</th>
142            </tr>
143            </thead>
144            <tbody>
145            <?php foreach ($data as $key => $value): ?>
146                <tr>
147                    <td><?= \htmlentities($key) ?></td>
148                    <td><pre><code class="language-php"><?=
149                                \htmlentities(Debugger::makeDebugValue($value))
150                ?></code></pre>
151                    </td>
152                </tr>
153            <?php endforeach ?>
154            </tbody>
155        </table>
156        <?php
157        return \ob_get_clean(); // @phpstan-ignore-line
158    }
159
160    protected function renderFlashNew() : string
161    {
162        $data = ArraySimple::value('$[flash][new]', $this->session->getAll());
163        if (empty($data)) {
164            return '';
165        }
166        \ob_start(); ?>
167        <h2>New</h2>
168        <p>Flash data available in the next request.</p>
169        <table>
170            <thead>
171            <tr>
172                <th>Key</th>
173                <th>Value</th>
174            </tr>
175            </thead>
176            <tbody>
177            <?php foreach ($data as $key => $value): ?>
178                <tr>
179                    <td><?= \htmlentities($key) ?></td>
180                    <td><pre><code class="language-php"><?=
181                                \htmlentities(Debugger::makeDebugValue($value))
182                ?></code></pre>
183                    </td>
184                </tr>
185            <?php endforeach ?>
186            </tbody>
187        </table>
188        <?php
189        return \ob_get_clean(); // @phpstan-ignore-line
190    }
191
192    protected function renderTemp() : string
193    {
194        $data = ArraySimple::value('$[temp]', $this->session->getAll());
195        if (empty($data)) {
196            return '<p>No temp data.</p>';
197        }
198        \ob_start(); ?>
199        <table>
200            <thead>
201            <tr>
202                <th>Key</th>
203                <th>Value</th>
204                <th>TTL</th>
205            </tr>
206            </thead>
207            <tbody>
208            <?php foreach ($data as $key => $value): ?>
209                <tr>
210                    <td><?= \htmlentities($key) ?></td>
211                    <td><pre><code class="language-php"><?=
212                                \htmlentities(Debugger::makeDebugValue($value))
213                ?></code></pre>
214                    </td>
215                    <td><?= \date('Y-m-d H:i:s', $value['ttl']) ?></td>
216                </tr>
217            <?php endforeach ?>
218            </tbody>
219        </table>
220        <?php
221        return \ob_get_clean(); // @phpstan-ignore-line
222    }
223
224    protected function renderSaveHandler() : string
225    {
226        \ob_start(); ?>
227        <p><strong>Save Handler:</strong> <?= \ini_get('session.save_handler') ?></p>
228        <p><strong>Serializer:</strong> <?= \ini_get('session.serialize_handler') ?></p>
229        <?php
230        if (isset($this->saveHandler)): ?>
231            <table>
232                <tbody>
233                <tr>
234                    <th>Class</th>
235                    <td><?= $this->saveHandler::class ?></td>
236                </tr>
237                <?php foreach ($this->getSaveHandlerConfigs() as $key => $value): ?>
238                    <?= $this->renderSaveHandlerTableRows($key, $value) ?>
239                <?php endforeach ?>
240                </tbody>
241            </table>
242        <?php
243        endif;
244        return \ob_get_clean(); // @phpstan-ignore-line
245    }
246
247    protected function renderSaveHandlerTableRows(string $key, mixed $value) : string
248    {
249        \ob_start(); ?>
250        <?php
251        if (\is_array($value)):
252            $count = \count($value); ?>
253            <tr>
254                <th rowspan="<?= $count ?>"><?= $key ?></th>
255                <td>
256                    <table>
257                        <?php foreach ($value[\array_key_first($value)] as $k => $v): ?>
258                            <tr>
259                                <th><?= \htmlentities($k) ?></th>
260                                <td><?= \htmlentities((string) $v) ?></td>
261                            </tr>
262                        <?php endforeach ?>
263                    </table>
264                </td>
265            </tr>
266            <?php
267            for ($i = 1; $i < $count; $i++): ?>
268                <tr>
269                    <td>
270                        <table>
271                            <?php foreach ($value[$i] as $k => $v): ?>
272                                <tr>
273                                    <th><?= \htmlentities($k) ?></th>
274                                    <td><?= \htmlentities((string) $v) ?></td>
275                                </tr>
276                            <?php endforeach ?>
277                        </table>
278                    </td>
279                </tr>
280            <?php
281            endfor;
282            return \ob_get_clean(); // @phpstan-ignore-line
283        endif; ?>
284        <tr>
285            <th><?= \htmlentities($key) ?></th>
286            <td><?= \htmlentities((string) $value) ?></td>
287        </tr>
288        <?php
289        return \ob_get_clean(); // @phpstan-ignore-line
290    }
291
292    protected function renderCookieParams() : string
293    {
294        $params = \session_get_cookie_params();
295        \ob_start(); ?>
296        <table>
297            <thead>
298            <tr>
299                <th>Lifetime</th>
300                <th>Path</th>
301                <th>Domain</th>
302                <th>Is Secure</th>
303                <th>Is HTTP Only</th>
304                <th>SameSite</th>
305            </tr>
306            </thead>
307            <tbody>
308            <tr>
309                <td><?= $params['lifetime'] ?></td>
310                <td><?= $params['path'] ?></td>
311                <td><?= $params['domain'] ?></td>
312                <td><?= $params['secure'] ? 'Yes' : 'No' ?></td>
313                <td><?= $params['httponly'] ? 'Yes' : 'No' ?></td>
314                <td><?= $params['samesite'] ?></td>
315            </tr>
316            </tbody>
317        </table>
318        <?php
319        return \ob_get_clean(); // @phpstan-ignore-line
320    }
321
322    protected function renderAutoRegenerateId() : string
323    {
324        if ($this->options['auto_regenerate_maxlifetime'] < 1) {
325            return '<p>Auto regenerate id is inactive.</p>';
326        }
327        $maxlifetime = (int) $this->options['auto_regenerate_maxlifetime'];
328        $regeneratedAt = ArraySimple::value('$[regenerated_at]', $this->session->getAll());
329        $nextRegeneration = $regeneratedAt + $maxlifetime;
330        \ob_start(); ?>
331        <table>
332            <thead>
333            <tr>
334                <th>Maxlifetime</th>
335                <th>Destroy</th>
336                <th>Regenerated At</th>
337                <th>Next Regeneration</th>
338            </tr>
339            </thead>
340            <tbody>
341            <tr>
342                <td><?= $maxlifetime ?></td>
343                <td><?= $this->options['auto_regenerate_destroy'] ? 'Yes' : 'No' ?></td>
344                <td><?= $regeneratedAt ? \date('Y-m-d H:i:s', $regeneratedAt) : '' ?></td>
345                <td><?= \date('Y-m-d H:i:s', $nextRegeneration) ?></td>
346            </tr>
347            </tbody>
348        </table>
349        <?php
350        return \ob_get_clean(); // @phpstan-ignore-line
351    }
352
353    /**
354     * @return array<string,mixed>
355     */
356    protected function getSaveHandlerConfigs() : array
357    {
358        $config = $this->saveHandler->getConfig();
359        if ($this->saveHandler instanceof FilesHandler) {
360            return [
361                'Directory' => $config['directory'],
362                'Prefix' => $config['prefix'],
363                'Match IP' => $config['match_ip'] ? 'Yes' : 'No',
364                'Match User-Agent' => $config['match_ua'] ? 'Yes' : 'No',
365            ];
366        }
367        if ($this->saveHandler instanceof MemcachedHandler) {
368            $servers = [];
369            foreach ($config['servers'] as $server) {
370                $servers[] = [
371                    'Host' => $server['host'],
372                    'Port' => $server['port'] ?? 11211,
373                    'Weight' => $server['weight'] ?? 0,
374                ];
375            }
376            return [
377                'Servers' => $servers,
378                'Prefix' => $config['prefix'],
379                'Lock Attempts' => $config['lock_attempts'],
380                'Lock Sleep' => $config['lock_sleep'],
381                'Lock TTL' => $config['lock_ttl'],
382                'Maxlifetime' => $config['maxlifetime'] ?? \ini_get('session.gc_maxlifetime'),
383                'Match IP' => $config['match_ip'] ? 'Yes' : 'No',
384                'Match User-Agent' => $config['match_ua'] ? 'Yes' : 'No',
385            ];
386        }
387        if ($this->saveHandler instanceof RedisHandler) {
388            return [
389                'Host' => $config['host'],
390                'Port' => $config['port'],
391                'Timeout' => $config['timeout'],
392                'Database' => $config['database'],
393                'Prefix' => $config['prefix'],
394                'Lock Attempts' => $config['lock_attempts'],
395                'Lock Sleep' => $config['lock_sleep'],
396                'Lock TTL' => $config['lock_ttl'],
397                'Maxlifetime' => $config['maxlifetime'] ?? \ini_get('session.gc_maxlifetime'),
398                'Match IP' => $config['match_ip'] ? 'Yes' : 'No',
399                'Match User-Agent' => $config['match_ua'] ? 'Yes' : 'No',
400            ];
401        }
402        if ($this->saveHandler instanceof DatabaseHandler) {
403            return [
404                'Host' => $config['host'],
405                'Schema' => $config['schema'],
406                'Table' => $config['table'],
407                'Columns' => [
408                    [
409                        'id' => $config['columns']['id'],
410                        'data' => $config['columns']['data'],
411                        'timestamp' => $config['columns']['timestamp'],
412                        'ip' => $config['columns']['ip'],
413                        'ua' => $config['columns']['ua'],
414                        'user_id' => $config['columns']['user_id'],
415                    ],
416                ],
417                'Maxlifetime' => $config['maxlifetime'] ?? \ini_get('session.gc_maxlifetime'),
418                'Match IP' => $config['match_ip'] ? 'Yes' : 'No',
419                'Match User-Agent' => $config['match_ua'] ? 'Yes' : 'No',
420                'Save IP' => $config['save_ip'] ? 'Yes' : 'No',
421                'Save User-Agent' => $config['save_ua'] ? 'Yes' : 'No',
422                'Save User Id' => $config['save_user_id'] ? 'Yes' : 'No',
423            ];
424        }
425        return [];
426    }
427}