1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BfwSql\Observers; |
4
|
|
|
|
5
|
|
|
use \Exception; |
6
|
|
|
use \BfwSql\Queries\Select; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Requests observer. |
10
|
|
|
* Create a log with SELECT requests executed and informations about it. Run an |
11
|
|
|
* EXPLAIN query on the request and add informations retured to log. |
12
|
|
|
* |
13
|
|
|
* @package bfw-sql |
14
|
|
|
* @author Vermeulen Maxime <[email protected]> |
15
|
|
|
* @version 2.0 |
16
|
|
|
*/ |
17
|
|
|
class Explain extends Basic |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* @const EXPLAIN_OK Explain status when the request has succeeded |
21
|
|
|
*/ |
22
|
|
|
const EXPLAIN_OK = 'ok'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @const EXPLAIN_FAILED Explain status when the request has failed |
26
|
|
|
*/ |
27
|
|
|
const EXPLAIN_FAILED = 'failed'; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @const EXPLAIN_EMPTY Explain status when the request has returned nothing |
31
|
|
|
*/ |
32
|
|
|
const EXPLAIN_EMPTY = 'empty'; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var \BfwSql\Sql The sql instanced used to run EXPLAIN request |
36
|
|
|
*/ |
37
|
|
|
protected $sql; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var object An object with explain status and informations returned |
41
|
|
|
*/ |
42
|
|
|
protected $explain; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Getter accessor to property sql |
46
|
|
|
* |
47
|
|
|
* @return \BfwSql\Sql |
48
|
|
|
*/ |
49
|
|
|
public function getSql(): \BfwSql\Sql |
50
|
|
|
{ |
51
|
|
|
return $this->sql; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Getter accessor to property explain |
56
|
|
|
* |
57
|
|
|
* @return object |
58
|
|
|
*/ |
59
|
|
|
public function getExplain() |
60
|
|
|
{ |
61
|
|
|
return $this->explain; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* {@inheritdoc} |
66
|
|
|
* Limited to "system query" event. |
67
|
|
|
*/ |
68
|
|
|
protected function analyzeUpdate() |
69
|
|
|
{ |
70
|
|
|
if ($this->action === 'system query') { |
71
|
|
|
$this->systemQuery(); |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* {@inheritdoc} |
77
|
|
|
* Limited to \BfwSql\Queries\Select object |
78
|
|
|
* We not run EXPLAIN automatically on other request type to not destroy db |
79
|
|
|
* |
80
|
|
|
* @return void |
81
|
|
|
*/ |
82
|
|
|
protected function systemQuery() |
83
|
|
|
{ |
84
|
|
|
if ($this->context instanceof \BfwSql\Executers\Common === false) { |
85
|
|
|
throw new Exception( |
86
|
|
|
'"system query" event should have an Executers\Common class' |
87
|
|
|
.' into the context.', |
88
|
|
|
self::ERR_SYSTEM_QUERY_CONTEXT_CLASS |
89
|
|
|
); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
if ($this->context->getQuery() instanceof Select === false) { |
93
|
|
|
return; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$this->explain = new class { |
97
|
|
|
public $status = \BfwSql\Observers\Explain::EXPLAIN_OK; |
98
|
|
|
public $datas = []; |
99
|
|
|
}; |
100
|
|
|
|
101
|
|
|
$this->sql = $this->obtainSql(); |
102
|
|
|
|
103
|
|
|
$this->runExplain(); |
104
|
|
|
return parent::systemQuery(); |
|
|
|
|
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Obtain a \BfwSql\Sql instance. |
109
|
|
|
* |
110
|
|
|
* @return \BfwSql\Sql |
111
|
|
|
*/ |
112
|
|
|
protected function obtainSql(): \BfwSql\Sql |
113
|
|
|
{ |
114
|
|
|
$sqlConnect = $this->context->getSqlConnect(); |
115
|
|
|
return new \BfwSql\Sql($sqlConnect); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Run the EXPLAIN request |
120
|
|
|
* |
121
|
|
|
* @return void |
122
|
|
|
*/ |
123
|
|
|
protected function runExplain() |
124
|
|
|
{ |
125
|
|
|
$this->sql->query('FLUSH STATUS;'); |
126
|
|
|
$pdo = $this->sql->getSqlConnect()->getPDO(); |
127
|
|
|
|
128
|
|
|
$explainQuery = 'EXPLAIN '.$this->context->getQuery()->assemble(); |
129
|
|
|
$request = $pdo->prepare( |
130
|
|
|
$explainQuery, |
131
|
|
|
$this->context->getPrepareDriversOptions() |
132
|
|
|
); |
133
|
|
|
$explainResult = $request->execute( |
134
|
|
|
$this->context->getQuery()->getPreparedParams() |
135
|
|
|
); |
136
|
|
|
|
137
|
|
|
if ($explainResult === false) { |
138
|
|
|
$this->explain->status = self::EXPLAIN_FAILED; |
139
|
|
|
return; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
$explainFetch = $request->fetch(\PDO::FETCH_ASSOC); |
143
|
|
|
if ($explainFetch === false) { |
144
|
|
|
$this->explain->status = self::EXPLAIN_EMPTY; |
145
|
|
|
return; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
foreach ($explainFetch as $explainKey => $explainValue) { |
149
|
|
|
$this->explain->datas[$explainKey] = $explainValue; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* {@inheritdoc} |
155
|
|
|
* Add explain informations to monolog too. |
156
|
|
|
*/ |
157
|
|
|
protected function addQueryToMonoLog( |
158
|
|
|
string $query, |
159
|
|
|
array $error, |
160
|
|
|
array $preparedArgs |
161
|
|
|
) { |
162
|
|
|
$logTxt = 'Type: '.$this->action.' ; ' |
163
|
|
|
.'Query: '.$query. ' ; ' |
164
|
|
|
.'Errors: '.print_r($error, true).' ; ' |
165
|
|
|
.'Explain status: '.$this->explain->status.' ; ' |
166
|
|
|
.'Explain datas: '.print_r($this->explain->datas, true). ';' |
167
|
|
|
; |
168
|
|
|
|
169
|
|
|
$this->monolog->getLogger()->debug($logTxt, $preparedArgs); |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.