Completed
Push — 2.0 ( bddf1c )
by Vermeulen
02:18
created

SqlObserver::analyzeUpdate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 0
1
<?php
2
3
namespace BfwSql;
4
5
/**
6
 * Requests observer.
7
 * Create a log with all EXPLAIN informations about SELECT requests executed.
8
 * 
9
 * @package bfw-sql
10
 * @author Vermeulen Maxime <[email protected]>
11
 * @version 2.0
12
 */
13
class SqlObserver implements \SplObserver
14
{
15
    /**
16
     * @var \SplFileObject $logFile The SplFileObject instance for the log file
17
     */
18
    protected $logFile;
19
    
20
    /**
21
     * @var \stdClass $observerConfig The "observer" part in config file
22
     */
23
    protected $observerConfig;
24
    
25
    /**
26
     * @var string $action The last action to send to observers
27
     */
28
    protected $action = '';
29
    
30
    /**
31
     * @var mixed $context The context to send to observers
32
     */
33
    protected $context = null;
34
    
35
    /**
36
     * Constructor
37
     * 
38
     * @param \stdClass $observerConfig The "observer" part in config file
39
     */
40
    public function __construct($observerConfig)
41
    {
42
        $this->observerConfig = $observerConfig;
43
        
44
        $this->logFile = new \SplFileObject($observerConfig->logFile, 'a+b');
45
    }
46
    
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function update(\SplSubject $subject)
51
    {
52
        $this->action  = $subject->getAction();
53
        $this->context = $subject->getContext();
54
        
55
        $this->analyzeUpdate();
56
    }
57
    
58
    /**
59
     * Analyze the update sent by subjects to search if the notify is for us
60
     * 
61
     * @return void
62
     */
63
    protected function analyzeUpdate()
64
    {
65
        if ($this->action !== 'BfwSqlRequest') {
66
            return;
67
        }
68
        
69
        if (get_class($this->context) !== '\BfwSql\SqlSelect') {
70
            return;
71
        }
72
        
73
        $this->runRequests();
74
    }
75
    
76
    /**
77
     * Run all requests to get informations about the SELECT request
78
     * 
79
     * @return void
80
     */
81
    protected function runRequests()
82
    {
83
        $this->addToLogFile('************* DEBUT OPTIMIZE SQL *************');
84
        $this->addToLogFile('BackTrace   : '.print_r($this->obtainBackTrace(), true));
85
        $this->addToLogFile('Requête SQL : '.$this->context->assemble());
86
        
87
        $sqlConnect = $this->context->getSqlConnect();
88
        $requestObj = new Sql($sqlConnect);
89
        
90
        $this->runExplain($requestObj);
91
        $this->runShowStatus($requestObj);
92
        
93
        $this->addToLogFile('************* FIN OPTIMIZE SQL *************');
94
        $this->addEmptyLineToLogFile();
95
    }
96
    
97
    /**
98
     * Obtain the backtrace to know informations about the caller of the
99
     * sql request.
100
     * 
101
     * @return string[]
102
     */
103
    protected function obtainBackTrace()
104
    {
105
        $backtrace      = [];
106
        $backtraceInfos = debug_backtrace();
107
        
108
        foreach($backtraceInfos as $trace)
109
        {
110
            $backtrace[] = $trace['file'].' : '.$trace['line'];
111
        }
112
        
113
        return $backtrace;
114
    }
115
    
116
    /**
117
     * Add a message to the observer log file
118
     * 
119
     * @param string $message
120
     * 
121
     * @return void
122
     */
123
    protected function addToLogFile($message)
124
    {
125
        $dateTime    = new \DateTime;
126
        $messageDate = $dateTime->format('Y-m-d H:i:s');
127
        
128
        $this->logFile->fwrite('['.$messageDate.'] '.$message."\n");
129
    }
130
    
131
    /**
132
     * Add a empty line to the log file
133
     * 
134
     * @return void
135
     */
136
    protected function addEmptyLineToLogFile()
137
    {
138
        $this->logFile->fwrite("\n");
139
    }
140
    
141
    /**
142
     * Run the EXPLAIN request and send informations to log file
143
     * 
144
     * @param \BfwSql\Sql $requestObj
145
     * 
146
     * @return void
147
     */
148
    protected function runExplain(\BfwSql\Sql $requestObj)
149
    {
150
        $requestObj->query('FLUSH STATUS;');
151
        
152
        $explainQuery  = 'EXPLAIN '.$this->context->assemble();
153
        $explainResult = $requestObj->query($explainQuery);
154
        
155
        if ($explainResult === false) {
156
            $this->addToLogFile('EXPLAIN failed');
157
            return;
158
        }
159
        
160
        $explainFetchAll = $explainResult->fetchAll();
161
        if (!isset($explainFetchAll[0])) {
162
            $this->addToLogFile('EXPLAIN VIDE');
163
            return;
164
        }
165
        
166
        $explainDatas = [];
167
        foreach($explainFetchAll[0] as $explainKey => $explainValue) {
168
            if (is_numeric($explainValue)) {
169
                continue;
170
            }
171
            
172
            $explainDatas[$explainKey] = $explainValue;
173
        }
174
        
175
        $this->addToLogFile(print_r($explainDatas, true));
176
    }
177
    
178
    /**
179
     * Run the SHOW STATUS request and send informations to log file
180
     * 
181
     * @param \BfwSql\Sql $requestObj
182
     * 
183
     * @return void
184
     */
185
    protected function runShowStatus(\BfwSql\Sql $requestObj)
186
    {
187
        $statusResult = $requestObj->query('SHOW STATUS');
188
        
189
        if ($statusResult === false) {
190
            $this->addToLogFile('SHOW STATUS failed');
191
            return;
192
        }
193
        
194
        $statusFetchAll = $statusResult->fetchAll();
195
        if (empty($statusFetchAll)) {
196
            $this->addToLogFile('EXPLAIN VIDE');
197
            return;
198
        }
199
        
200
        $statusDatas = [];
201
        foreach($statusFetchAll as $statusRow) {
202
            $statusKey   = $statusRow['Variable_name'];
203
            $statusValue = $statusRow['Value'];
204
205
            if (
206
                substr($statusKey, 0, 8) === 'Created_'
207
                || substr($statusKey, 0, 8) === 'Handler_'
208
            ) {
209
                $statusDatas[$statusKey] = $statusValue;
210
            }
211
        }
212
        
213
        $this->addToLogFile(print_r($statusDatas, true));
214
    }
215
}
216