Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
132 / 132
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
AppCollector
100.00% covered (success)
100.00%
132 / 132
100.00% covered (success)
100.00%
10 / 10
33
100.00% covered (success)
100.00%
1 / 1
 setApp
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 setStartTime
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setEndTime
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setStartMemory
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setEndMemory
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getActivities
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
3
 getContents
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
3
 renderLoadedServices
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
6
 getServices
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 renderAvailableServices
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
1 / 1
11
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework MVC 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\MVC\Debug;
11
12use Framework\Debug\Collector;
13use Framework\Debug\Debugger;
14use Framework\MVC\App;
15use ReflectionClass;
16use ReflectionMethod;
17
18/**
19 * Class AppCollector.
20 *
21 * @package mvc
22 */
23class AppCollector extends Collector
24{
25    protected App $app;
26    protected float $startTime;
27    protected float $endTime;
28    protected int $startMemory;
29    protected int $endMemory;
30
31    public function setApp(App $app) : static
32    {
33        $this->app = $app;
34        if ( ! isset($this->startTime)) {
35            $this->setStartTime();
36        }
37        if ( ! isset($this->startMemory)) {
38            $this->setStartMemory();
39        }
40        return $this;
41    }
42
43    public function setStartTime(float $microtime = null) : static
44    {
45        $this->startTime = $microtime ?? \microtime(true);
46        return $this;
47    }
48
49    public function setEndTime(float $microtime = null) : static
50    {
51        $this->endTime = $microtime ?? \microtime(true);
52        return $this;
53    }
54
55    public function setStartMemory(int $memoryUsage = null) : static
56    {
57        $this->startMemory = $memoryUsage ?? \memory_get_usage();
58        return $this;
59    }
60
61    public function setEndMemory(int $memoryUsage = null) : static
62    {
63        $this->endMemory = $memoryUsage ?? \memory_get_usage();
64        return $this;
65    }
66
67    public function getActivities() : array
68    {
69        $activities = [];
70        $activities[] = [
71            'collector' => $this->getName(),
72            'class' => static::class,
73            'description' => 'Runtime',
74            'start' => $this->startTime,
75            'end' => $this->endTime,
76        ];
77        foreach ($this->getServices() as $service => $data) {
78            foreach ($data as $item) {
79                $activities[] = [
80                    'collector' => $this->getName(),
81                    'class' => static::class,
82                    'description' => 'Load service ' . $service . ':' . $item['name'],
83                    'start' => $item['start'],
84                    'end' => $item['end'],
85                ];
86            }
87        }
88        return $activities;
89    }
90
91    public function getContents() : string
92    {
93        if ( ! isset($this->endTime)) {
94            $this->setEndTime(\microtime(true));
95        }
96        if ( ! isset($this->endMemory)) {
97            $this->setEndMemory(\memory_get_usage());
98        }
99        \ob_start(); ?>
100        <p><strong>Started at:</strong> <?= \date('r', (int) $this->startTime) ?></p>
101        <p><strong>Runtime:</strong> <?= \round($this->endTime - $this->startTime, 6) ?> seconds
102        </p>
103        <p>
104            <strong>Memory:</strong> <?=
105            Debugger::convertSize($this->endMemory - $this->startMemory) ?>
106        </p>
107        <h1>Services</h1>
108        <h2>Loaded Service Instances</h2>
109        <?= $this->renderLoadedServices() ?>
110        <h2>Available Services</h2>
111        <?php
112        echo $this->renderAvailableServices();
113        return \ob_get_clean(); // @phpstan-ignore-line
114    }
115
116    protected function renderLoadedServices() : string
117    {
118        $services = $this->getServices();
119        $total = 0;
120        foreach ($services as $data) {
121            $total += \count($data);
122        }
123        if ($total === 0) {
124            return '<p>No service instance has been loaded.</p>';
125        }
126        \ob_start(); ?>
127        <p>Total of <?= $total ?> service instance<?= $total !== 1 ? 's' : '' ?> loaded.</p>
128        <table>
129            <thead>
130            <tr>
131                <th>Service</th>
132                <th>Instances</th>
133                <th title="Seconds">Time to Load</th>
134            </tr>
135            </thead>
136            <tbody>
137            <?php foreach ($services as $service => $data): ?>
138                <?php $count = \count($data) ?>
139                <tr>
140                    <td rowspan="<?= $count ?>"><?= $service ?></td>
141                    <td><?= $data[0]['name'] ?></td>
142                    <td><?= \round($data[0]['end'] - $data[0]['start'], 6) ?></td>
143                </tr>
144                <?php for ($i = 1; $i < $count; $i++): ?>
145                    <tr>
146                        <td><?= $data[$i]['name'] ?></td>
147                        <td><?= \round($data[$i]['end'] - $data[$i]['start'], 6) ?></td>
148                    </tr>
149                <?php endfor ?>
150            <?php endforeach ?>
151            </tbody>
152        </table>
153        <?php
154        return \ob_get_clean(); // @phpstan-ignore-line
155    }
156
157    /**
158     * @return array<string,mixed>
159     */
160    protected function getServices() : array
161    {
162        $result = [];
163        foreach ($this->getData() as $data) {
164            if ( ! isset($result[$data['service']])) {
165                $result[$data['service']] = [];
166            }
167            $result[$data['service']][] = [
168                'name' => $data['instance'],
169                'start' => $data['start'],
170                'end' => $data['end'],
171            ];
172        }
173        return $result; // @phpstan-ignore-line
174    }
175
176    protected function renderAvailableServices() : string
177    {
178        \ob_start();
179        $services = [];
180        $class = new ReflectionClass($this->app);
181        $methods = $class->getMethods(ReflectionMethod::IS_STATIC);
182        foreach ($methods as $method) {
183            if ( ! $method->isPublic()) {
184                continue;
185            }
186            $name = $method->getName();
187            if (\in_array($name, [
188                'config',
189                'getService',
190                'setService',
191                'removeService',
192                'isCli',
193                'setIsCli',
194                'isDebugging',
195            ], true)) {
196                continue;
197            }
198            $param = $method->getParameters()[0] ?? null;
199            if ( ! $param || $param->getName() !== 'instance') {
200                continue;
201            }
202            if ($param->getType()?->getName() !== 'string') { // @phpstan-ignore-line
203                continue;
204            }
205            $instances = [];
206            if ($param->isDefaultValueAvailable()) {
207                $instances[] = $param->getDefaultValue();
208            }
209            foreach ((array) $this->app::config()->getInstances($name) as $inst => $s) {
210                $instances[] = $inst;
211            }
212            $instances = \array_unique($instances);
213            \sort($instances);
214            $services[$name] = [
215                'returnType' => $method->getReturnType()?->getName(), // @phpstan-ignore-line
216                'instances' => $instances,
217            ];
218        }
219        \ksort($services);
220        $countServices = \count($services);
221        $s = 0; ?>
222        <p>There are <?= $countServices ?> services available.</p>
223        <table>
224            <thead>
225            <tr>
226                <th>#</th>
227                <th>Service</th>
228                <th>Config Instances</th>
229                <th>Return Type</th>
230            </tr>
231            </thead>
232            <tbody>
233            <?php foreach ($services as $name => $data): ?>
234                <?php $count = \count($data['instances']) ?>
235                <tr>
236                    <td rowspan="<?= $count ?>"><?= ++$s ?></td>
237                    <td rowspan="<?= $count ?>"><?= $name ?></td>
238                    <td><?= $data['instances'][0] ?></td>
239                    <td rowspan="<?= $count ?>"><?= $data['returnType'] ?></td>
240                </tr>
241                <?php for ($i = 1; $i < $count; $i++): ?>
242                    <tr>
243                        <td><?= $data['instances'][$i] ?></td>
244                    </tr>
245                <?php endfor ?>
246            <?php endforeach ?>
247            </tbody>
248        </table>
249        <?php
250        return \ob_get_clean(); // @phpstan-ignore-line
251    }
252}