Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
79 / 79
100.00% covered (success)
100.00%
15 / 15
CRAP
100.00% covered (success)
100.00%
1 / 1
Debugger
100.00% covered (success)
100.00%
79 / 79
100.00% covered (success)
100.00%
15 / 15
25
100.00% covered (success)
100.00%
1 / 1
 addCollection
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCollections
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCollection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addCollector
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 setOptions
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getActivities
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
5
 addActivityValues
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 setDebugbarView
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 getDebugbarView
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderDebugbar
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 makeSafeName
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 convertSize
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 makeDebugValue
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 roundVersion
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Debug 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\Debug;
11
12use Framework\Helpers\Isolation;
13use InvalidArgumentException;
14
15/**
16 * Class Debugger.
17 *
18 * @package debug
19 */
20class Debugger
21{
22    /**
23     * @var array<string,Collection>
24     */
25    protected array $collections = [];
26    /**
27     * @var array<string,mixed>
28     */
29    protected array $options = [];
30    protected string $debugbarView = __DIR__ . '/Views/debugbar/debugbar.php';
31
32    public function addCollection(Collection $collection) : static
33    {
34        $this->collections[$collection->getName()] = $collection;
35        return $this;
36    }
37
38    /**
39     * @return array<string,Collection>
40     */
41    public function getCollections() : array
42    {
43        return $this->collections;
44    }
45
46    public function getCollection(string $name) : ?Collection
47    {
48        return $this->getCollections()[$name] ?? null;
49    }
50
51    public function addCollector(Collector $collector, string $collectionName) : static
52    {
53        $collection = $this->getCollection($collectionName);
54        if ($collection === null) {
55            $collection = new Collection($collectionName);
56            $this->addCollection($collection);
57        }
58        $collection->addCollector($collector);
59        return $this;
60    }
61
62    /**
63     * @param array<string,mixed> $options
64     *
65     * @return static
66     */
67    public function setOptions(array $options) : static
68    {
69        $this->options = $options;
70        return $this;
71    }
72
73    /**
74     * @return array<string,mixed>
75     */
76    public function getOptions() : array
77    {
78        return $this->options;
79    }
80
81    /**
82     * @return array<string,mixed>
83     */
84    public function getActivities() : array
85    {
86        $collected = [];
87        foreach ($this->getCollections() as $collection) {
88            foreach ($collection->getActivities() as $activities) {
89                $collected = [...$collected, ...$activities];
90            }
91        }
92        $min = .0;
93        $max = .0;
94        if ($collected) {
95            \usort($collected, static function ($c1, $c2) {
96                return $c1['start'] <=> $c2['start'];
97            });
98            $min = \min(\array_column($collected, 'start'));
99            $max = \max(\array_column($collected, 'end'));
100            foreach ($collected as &$activity) {
101                $this->addActivityValues($activity, $min, $max);
102            }
103        }
104        return [
105            'min' => $min,
106            'max' => $max,
107            'total' => $max - $min,
108            'collected' => $collected,
109        ];
110    }
111
112    /**
113     * @param array<string,mixed> $activity
114     * @param float $min
115     * @param float $max
116     */
117    protected function addActivityValues(array &$activity, float $min, float $max) : void
118    {
119        $total = $max - $min;
120        $activity['total'] = $activity['end'] - $activity['start'];
121        $activity['left'] = \round(($activity['start'] - $min) * 100 / $total, 3);
122        $activity['width'] = \round($activity['total'] * 100 / $total, 3);
123    }
124
125    public function setDebugbarView(string $file) : static
126    {
127        $realpath = \realpath($file);
128        if ( ! $realpath || ! \is_file($realpath)) {
129            throw new InvalidArgumentException(
130                'Invalid debugbar view file: ' . $file
131            );
132        }
133        $this->debugbarView = $realpath;
134        return $this;
135    }
136
137    public function getDebugbarView() : string
138    {
139        return $this->debugbarView;
140    }
141
142    public function renderDebugbar() : string
143    {
144        \ob_start();
145        Isolation::require($this->getDebugbarView(), [
146            'collections' => $this->getCollections(),
147            'activities' => $this->getActivities(),
148            'options' => $this->getOptions(),
149        ]);
150        return \ob_get_clean(); // @phpstan-ignore-line
151    }
152
153    public static function makeSafeName(string $name) : string
154    {
155        return \strtr(\trim(\strip_tags(\strtolower($name))), [
156            'ยท' => '-',
157            ':' => '-',
158            '(' => '-',
159            ')' => '-',
160            '/' => '-',
161            '\\' => '-',
162            ' ' => '-',
163        ]);
164    }
165
166    public static function convertSize(float | int $size) : string
167    {
168        if (empty($size)) {
169            return '0 B';
170        }
171        $unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
172        $index = \floor(\log($size, 1024));
173        return \round($size / (1024 ** $index), 3) . ' ' . $unit[$index];
174    }
175
176    public static function makeDebugValue(mixed $value) : string
177    {
178        $type = \get_debug_type($value);
179        return (string) match ($type) {
180            'array' => 'array',
181            'bool' => $value ? 'true' : 'false',
182            'float', 'int' => $value,
183            'null' => 'null',
184            'string' => "'" . \strtr($value, ["'" => "\\'"]) . "'",
185            default => 'instanceof ' . $type,
186        };
187    }
188
189    /**
190     * Remove dots and zeros from the end of the version.
191     *
192     * @param string $version
193     *
194     * @return string
195     */
196    public static function roundVersion(string $version) : string
197    {
198        if (\str_ends_with($version, '.0')) {
199            $version = \substr($version, 0, -2);
200            return static::roundVersion($version);
201        }
202        return $version;
203    }
204}