Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
80.52% |
62 / 77 |
|
80.00% |
8 / 10 |
CRAP | |
0.00% |
0 / 1 |
QueryHandler | |
80.52% |
62 / 77 |
|
80.00% |
8 / 10 |
38.10 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setProfiler | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
hasProfiler | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getProfiler | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
profiler | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
prepare | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
2 | |||
prepareHeaderAsString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
prepareAsString | |
58.33% |
14 / 24 |
|
0.00% |
0 / 1 |
14.86 | |||
log | |
78.26% |
18 / 23 |
|
0.00% |
0 / 1 |
12.24 | |||
__get | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Debug\Handler; |
15 | |
16 | use Pop\Db\Adapter\Profiler\Profiler; |
17 | use Pop\Log\Logger; |
18 | |
19 | /** |
20 | * Debug query handler class |
21 | * |
22 | * @category Pop |
23 | * @package Pop\Debug |
24 | * @author Nick Sagona, III <dev@nolainteractive.com> |
25 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
26 | * @license http://www.popphp.org/license New BSD License |
27 | * @version 2.2.0 |
28 | */ |
29 | class QueryHandler extends AbstractHandler |
30 | { |
31 | |
32 | /** |
33 | * Profiler |
34 | * @var ?Profiler |
35 | */ |
36 | protected ?Profiler $profiler = null; |
37 | |
38 | /** |
39 | * Constructor |
40 | * |
41 | * Instantiate a query handler object |
42 | * |
43 | * @param ?Profiler $profiler |
44 | * @param ?string $name |
45 | * @param ?Logger $logger |
46 | * @param array $loggingParams |
47 | */ |
48 | public function __construct(?Profiler $profiler = null, ?string $name = null, ?Logger $logger = null, array $loggingParams = []) |
49 | { |
50 | parent::__construct($name, $logger, $loggingParams); |
51 | |
52 | if ($profiler !== null) { |
53 | $this->setProfiler($profiler); |
54 | } |
55 | } |
56 | |
57 | /** |
58 | * Set profiler |
59 | * |
60 | * @param Profiler $profiler |
61 | * @return QueryHandler |
62 | */ |
63 | public function setProfiler(Profiler $profiler): QueryHandler |
64 | { |
65 | $this->profiler = $profiler; |
66 | return $this; |
67 | } |
68 | |
69 | /** |
70 | * Determine if the handler has a profiler |
71 | * |
72 | * @return bool |
73 | */ |
74 | public function hasProfiler(): bool |
75 | { |
76 | return ($this->profiler !== null); |
77 | } |
78 | |
79 | /** |
80 | * Get profiler |
81 | * |
82 | * @return Profiler |
83 | */ |
84 | public function getProfiler(): Profiler |
85 | { |
86 | return $this->profiler; |
87 | } |
88 | |
89 | /** |
90 | * Get profiler (alias method) |
91 | * |
92 | * @return Profiler |
93 | */ |
94 | public function profiler(): Profiler |
95 | { |
96 | return $this->profiler; |
97 | } |
98 | |
99 | /** |
100 | * Prepare handler data for storage |
101 | * |
102 | * @return array |
103 | */ |
104 | public function prepare(): array |
105 | { |
106 | $elapsed = $this->profiler->getElapsed(); |
107 | $data = [ |
108 | 'start' => number_format((float)$this->profiler->getStart(), 5, '.', ''), |
109 | 'finish' => number_format((float)$this->profiler->getFinish(), 5, '.', ''), |
110 | 'elapsed' => $elapsed, |
111 | 'steps' => [] |
112 | ]; |
113 | |
114 | foreach ($this->profiler->getSteps() as $step) { |
115 | $data['steps'][] = [ |
116 | 'start' => number_format($step->getStart(), 5, '.', ''), |
117 | 'finish' => number_format($step->getFinish(), 5, '.', ''), |
118 | 'elapsed' => $step->getElapsed(), |
119 | 'query' => $step->getQuery(), |
120 | 'params' => $step->getParams(), |
121 | 'errors' => $step->getErrors() |
122 | ]; |
123 | } |
124 | |
125 | return $data; |
126 | } |
127 | |
128 | /** |
129 | * Prepare header string |
130 | * |
131 | * @return string |
132 | */ |
133 | public function prepareHeaderAsString(): string |
134 | { |
135 | $string = ((!empty($this->name)) ? $this->name . ' ' : '') . 'Query Handler'; |
136 | $string .= PHP_EOL . str_repeat('=', strlen($string)) . PHP_EOL; |
137 | |
138 | return $string; |
139 | } |
140 | |
141 | /** |
142 | * Prepare handler data as string |
143 | * |
144 | * @return string |
145 | */ |
146 | public function prepareAsString(): string |
147 | { |
148 | $elapsed = $this->profiler->getElapsed(); |
149 | $string = "Start:\t\t\t" . number_format((float)$this->profiler->getStart(), 5, '.', '') . PHP_EOL; |
150 | $string .= "Finish:\t\t\t" . number_format((float)$this->profiler->getFinish(), 5, '.', '') . PHP_EOL; |
151 | $string .= "Elapsed:\t\t" . $elapsed . ' seconds' . PHP_EOL . PHP_EOL; |
152 | |
153 | $string .= "Queries:" . PHP_EOL; |
154 | $string .= "--------" . PHP_EOL; |
155 | foreach ($this->profiler->getSteps() as $step) { |
156 | $string .= $step->getQuery() . ' [' . $step->getElapsed() . ']' . PHP_EOL; |
157 | $string .= "Start:\t\t\t" . number_format($step->getStart(), 5, '.', '') . PHP_EOL; |
158 | $string .= "Finish:\t\t\t" . number_format($step->getFinish(), 5, '.', '') . PHP_EOL; |
159 | if ($step->hasParams()) { |
160 | $string .= "Params:" . PHP_EOL; |
161 | foreach ($step->getParams() as $name => $value) { |
162 | if (is_array($value)) { |
163 | foreach ($value as $v) { |
164 | $string .= "\t" . $name . ' => ' . $v . PHP_EOL; |
165 | } |
166 | } else { |
167 | $string .= "\t" . $name . ' => ' . $value . PHP_EOL; |
168 | } |
169 | } |
170 | } |
171 | if ($step->hasErrors()) { |
172 | $string .= "Errors:" . PHP_EOL; |
173 | foreach ($step->getErrors() as $time => $error) { |
174 | $string .= "\t[" . number_format($time, 5, '.', '') . "]" . $error['error'] . |
175 | ((!empty($error['number'])) ? ' [' . $error['number'] . ']' : '') . PHP_EOL; |
176 | } |
177 | |
178 | } |
179 | $string .= PHP_EOL; |
180 | } |
181 | |
182 | return $string; |
183 | } |
184 | |
185 | /** |
186 | * Trigger handler logging |
187 | * |
188 | * @throws Exception |
189 | * @return void |
190 | */ |
191 | public function log(): void |
192 | { |
193 | if (($this->hasLogger()) && ($this->hasLoggingParams())) { |
194 | $logLevel = $this->loggingParams['level'] ?? null; |
195 | $useContext = $this->loggingParams['context'] ?? null; |
196 | $timeLimit = $this->loggingParams['limit'] ?? null; |
197 | |
198 | if ($logLevel !== null) { |
199 | if ($timeLimit !== null) { |
200 | foreach ($this->profiler->getSteps() as $step) { |
201 | $elapsedTime = $step->getElapsed(); |
202 | if ($elapsedTime >= $timeLimit) { |
203 | $this->logger->log($logLevel, 'A query has exceeded the time limit of ' . $timeLimit . |
204 | ' second(s) by ' . $elapsedTime - $timeLimit . ' second(s). The query execution was a total of ' . |
205 | $elapsedTime . ' second(s).' |
206 | ); |
207 | } |
208 | } |
209 | } else { |
210 | $context = []; |
211 | $message = (count($this->profiler->getSteps()) > 1) ? |
212 | '(' . count($this->profiler->getSteps()) . ') new queries have been executed.' : |
213 | '(1) new query has been executed.'; |
214 | |
215 | $context['queries'] = (($useContext !== null) && (strtolower($useContext) == 'text')) ? |
216 | $this->prepareAsString() : $this->prepare(); |
217 | |
218 | if (is_string($useContext)) { |
219 | $context['format'] = $useContext; |
220 | } |
221 | |
222 | $this->logger->log($logLevel, $message, $context); |
223 | } |
224 | } else { |
225 | throw new Exception('Error: The log level parameter was not set.'); |
226 | } |
227 | } |
228 | } |
229 | |
230 | /** |
231 | * Magic get method to return the profiler. |
232 | * |
233 | * @param string $name |
234 | * @return mixed |
235 | */ |
236 | public function __get(string $name): mixed |
237 | { |
238 | return match ($name) { |
239 | 'profiler' => $this->profiler, |
240 | default => null, |
241 | }; |
242 | } |
243 | |
244 | } |