Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
QueryHandler
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
9 / 9
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setProfiler
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasProfiler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getProfiler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 profiler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 prepare
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
2
 prepareMessage
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 log
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
 __get
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Debug\Handler;
16
17use Pop\Db\Adapter\Profiler\Profiler;
18use Psr\Log\LoggerInterface;
19
20/**
21 * Debug query handler class
22 *
23 * @category   Pop
24 * @package    Pop\Debug
25 * @author     Nick Sagona, III <dev@noladev.com>
26 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
27 * @license    https://www.popphp.org/license     New BSD License
28 * @version    4.0.0
29 */
30class QueryHandler extends AbstractHandler
31{
32
33    /**
34     * Profiler
35     * @var ?Profiler
36     */
37    protected ?Profiler $profiler = null;
38
39    /**
40     * Constructor
41     *
42     * Instantiate a query handler object
43     *
44     * @param ?Profiler        $profiler
45     * @param ?string          $name
46     * @param ?LoggerInterface $logger
47     * @param array            $loggingParams
48     */
49    public function __construct(?Profiler $profiler = null, ?string $name = null, ?LoggerInterface $logger = null, array $loggingParams = [])
50    {
51        parent::__construct($name, $logger, $loggingParams);
52
53        if ($profiler !== null) {
54            $this->setProfiler($profiler);
55        }
56    }
57
58    /**
59     * Set profiler
60     *
61     * @param  Profiler $profiler
62     * @return QueryHandler
63     */
64    public function setProfiler(Profiler $profiler): QueryHandler
65    {
66        $this->profiler = $profiler;
67        return $this;
68    }
69
70    /**
71     * Determine if the handler has a profiler
72     *
73     * @return bool
74     */
75    public function hasProfiler(): bool
76    {
77        return ($this->profiler !== null);
78    }
79
80    /**
81     * Get profiler
82     *
83     * @return Profiler
84     */
85    public function getProfiler(): Profiler
86    {
87        return $this->profiler;
88    }
89
90    /**
91     * Get profiler (alias method)
92     *
93     * @return Profiler
94     */
95    public function profiler(): Profiler
96    {
97        return $this->profiler;
98    }
99
100    /**
101     * Prepare handler data for storage
102     *
103     * @return array
104     */
105    public function prepare(): array
106    {
107        $this->setStart((float)$this->profiler->getStart());
108        $this->setEnd((float)$this->profiler->getFinish());
109        $this->setElapsed((float)$this->profiler->getElapsed());
110
111        $data = [
112            'start'   => number_format((float)$this->getStart(), 5, '.', ''),
113            'end'     => number_format((float)$this->getEnd(), 5, '.', ''),
114            'elapsed' => $this->getElapsed(),
115            'steps'   => []
116        ];
117
118        foreach ($this->profiler->getSteps() as $step) {
119            $data['steps'][] = [
120                'start'   => number_format($step->getStart(), 5, '.', ''),
121                'end'     => number_format($step->getFinish(), 5, '.', ''),
122                'elapsed' => $step->getElapsed(),
123                'query'   => $step->getQuery(),
124                'params'  => $step->getParams(),
125                'errors'  => $step->getErrors()
126            ];
127        }
128
129        return $data;
130    }
131
132    /**
133     * Prepare handler message
134     *
135     * @param  ?array $context
136     * @return string
137     */
138    public function prepareMessage(?array $context = null): string
139    {
140        if ($context === null) {
141            $context = $this->prepare();
142        }
143
144        return (!empty($context['steps']) && (count($context['steps']) > 1)) ?
145            '(' . count($context['steps']) . ') new queries have been executed.' :
146            '(1) new query has been executed.';
147    }
148
149    /**
150     * Trigger handler logging
151     *
152     * @throws Exception
153     * @return void
154     */
155    public function log(): void
156    {
157        $logLevel = $this->resolveLogLevel();
158        if ($logLevel === null) {
159            return;
160        }
161
162        $timeLimit = $this->loggingParams['limit'] ?? null;
163        $context   = $this->prepare();
164
165        if ($timeLimit !== null) {
166            foreach ($context['steps'] as $step) {
167                $elapsedTime = $step['elapsed'] ?? 0;
168                if ($elapsedTime >= $timeLimit) {
169                    $this->logger->log($logLevel, 'A query has exceeded the time limit of ' . $timeLimit .
170                        ' second(s) by ' . $elapsedTime - $timeLimit . ' second(s). The query execution was a total of ' .
171                        $elapsedTime . ' second(s).', $context
172                    );
173                }
174            }
175        } else {
176            $this->logger->log($logLevel, $this->prepareMessage($context), $context);
177        }
178    }
179
180    /**
181     * Magic get method to return the profiler.
182     *
183     * @param  string $name
184     * @return mixed
185     */
186    public function __get(string $name): mixed
187    {
188        return match ($name) {
189            'profiler' => $this->profiler,
190            default    => null,
191        };
192    }
193
194}