Completed
Pull Request — master (#268)
by
unknown
01:25
created

Xhgui_Storage_PDO::drop()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
class Xhgui_Storage_PDO implements \Xhgui_StorageInterface, \Xhgui_WatchedFunctionsStorageInterface {
3
4
    /**
5
     * @var \PDO
6
     */
7
    protected $connection;
8
9
    /**
10
     * PDO constructor.
11
     * @param $config
12
     */
13
    public function __construct($config)
14
    {
15
        $this->connection = new \PDO(
16
            $config['db.dsn'],
17
            !empty($config['db.user'])      ? $config['db.user'] : null,
18
            !empty($config['db.password'])  ? $config['db.password'] : null,
19
            !empty($config['db.options'])   ? $config['db.options'] : []
20
        );
21
        $this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
22
    }
23
24
    /**
25
     * @param \Xhgui_Storage_Filter $filter
26
     * @param bool $projections
27
     * @return \Xhgui_Storage_ResultSet
28
     */
29
    public function find(\Xhgui_Storage_Filter $filter, $projections = false)
30
    {
31
        list($query, $params) = $this->getQuery($filter, false);
32
//        print '<pre>';
33
//        print_r($query);
34
//        print_r($params);
35
//        print '</pre>';
36
//        exit;
37
        try {
38
            $stmt = $this->connection->prepare($query);
39
            $stmt->execute($params);
40
41
        } catch (\Exception $e) {
42
            print_r($e->getMessage());
43
            exit;
44
        }
45
46
        $tmp = [];
47
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
48
            $tmp[$row['id']] = $row;
49
            $tmp[$row['id']]['profile']    = json_decode($row['profiles'], true);
50
            $tmp[$row['id']]['meta']       = json_decode($row['meta'], true);
51
        }
52
        
53
        return new \Xhgui_Storage_ResultSet($tmp);
54
    }
55
56
    /**
57
     * @param \Xhgui_Storage_Filter $filter
58
     * @param bool $count
59
     * @return array
60
     */
61
    protected function getQuery(\Xhgui_Storage_Filter $filter, $count = false) {
62
        $params = [];
63
64
        if ($count === true) {
65
            $columns = ' count(*) as c ';
66
        } else {
67
            $columns = ' p.*, i.*, m.*, p.profile_id as _id, main_wt as duration ';
68
        }
69
70
        $sql = "
71
select 
72
    $columns
73
from 
74
    profiles as p left join 
75
    profiles_info as i on (p.profile_id = i.id) LEFT JOIN
76
    profiles_meta as m on (p.profile_id = m.profile_id)
77
";
78
79
        $where = [];
80
81
        foreach([
82
            'url'               => 'url',
83
            'method'            => 'method',
84
            'application'       => 'application',
85
            'version'           => 'version',
86
            'branch'            => 'branch',
87
            'controller'        => 'controller',
88
            'action'            => 'action',
89
            ] as $dbField => $field) {
90
91
            $method = 'get'.ucfirst($field);
92
93
            if ($filter->{$method}()) {
94
                $where[]        = ' '.$dbField.' = :'.$field.' ';
95
                $params[$field]  = $filter->{$method}();
96
            }
97
        }
98
        
99
        if ($filter->getStartDate()) {
100
            $where[]                = ' request_time >= datetime(:startDate)';
101
            $params['startDate']   = $this->getDateTimeFromString($filter->getStartDate())->format('Y-m-d H:i:s');
0 ignored issues
show
Documentation introduced by
$filter->getStartDate() is of type object<DateTime>, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
102
        }
103
104
        if ($filter->getEndDate()) {
105
            $where[]                = ' request_time <= datetime(:endDate)';
106
            $params['endDate']   = $this->getDateTimeFromString($filter->getEndDate())->format('Y-m-d H:i:s');
0 ignored issues
show
Documentation introduced by
$filter->getEndDate() is of type object<DateTime>, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
        }
108
109
        if (!empty($where)) {
110
            $sql .= ' WHERE '.join(' AND ', $where);
111
        }
112
113
        if ($count === true) {
114
            return [$sql, $params];
115
        }
116
117
        switch ($filter->getSort()) {
118
            case 'ct':
119
                $sql .= ' order by main_ct';
120
                break;
121
122
            case 'wt':
123
                $sql .= ' order by main_wt';
124
                break;
125
126
            case 'cpu':
127
                $sql .= ' order by main_cpu';
128
                break;
129
130
            case 'mu':
131
                $sql .= ' order by main_mu';
132
                break;
133
134
            case 'pmu':
135
                $sql .= ' order by main_pmu';
136
                break;
137
138
            case 'controller':
139
                $sql .= ' order by controller';
140
                break;
141
142
            case 'action':
143
                $sql .= ' order by action';
144
                break;
145
146
            case 'application':
147
                $sql .= ' order by application';
148
                break;
149
150
            case 'branch':
151
                $sql .= ' order by branch';
152
                break;
153
154
            case 'version':
155
                $sql .= ' order by version';
156
                break;
157
158
            case 'time':
159
            default:
160
                $sql .= ' order by request_time';
161
                break;
162
        }
163
164
        switch ($filter->getDirection()) {
165
            case 'asc':
166
                $sql .= ' asc ';
167
                break;
168
169
            default:
170
            case 'desc':
0 ignored issues
show
Unused Code introduced by
case 'desc': $sql .= ' desc '; break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
171
                $sql .= ' desc ';
172
                break;
173
        }
174
175
        if ($filter->getPerPage()) {
176
            $sql            .= ' LIMIT :limit ';
177
            $params['limit'] = $filter->getPerPage();
178
        }
179
180
        if ($filter->getPage()) {
181
            $sql                .= ' OFFSET :offset ';
182
            $params['offset']   = ($filter->getPerPage()*($filter->getPage()-1));
183
        }
184
185
        return [$sql, $params];
186
    }
187
188
    public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1)
189
    {
190
        $stmt = $this->connection->prepare('select 
191
    * 
192
from 
193
    profiles_info
194
where 
195
    url = :url
196
');
197
        $stmt->execute(['url'=> $filter->getUrl()]);
198
        $aggregatedData = [];
199
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
200
            $date = new \DateTime($row['request_time']);
201
            $formattedDate = $date->format('Y-m-d');
202
            if (empty($aggregatedData[$date->format('Y-m-d')])) {
203
                $aggregatedData[$date->format('Y-m-d')] = [
204
                    'wall_times'    => [],
205
                    'cpu_times'     => [],
206
                    'mu_times'      => [],
207
                    'pmu_times'     => [],
208
                    'row_count'     => 0
209
                ];
210
            }
211
212
            $aggregatedData[$formattedDate]['wall_times'][] = $row['main_wt'];
213
            $aggregatedData[$formattedDate]['cpu_times'][]  = $row['main_ct'];
214
            $aggregatedData[$formattedDate]['mu_times'][]   = $row['main_mu'];
215
            $aggregatedData[$formattedDate]['pmu_times'][]  = $row['main_pmu'];
216
            $aggregatedData[$formattedDate]['row_count']++;
217
            $aggregatedData[$formattedDate]['_id']          = $formattedDate;
218
            $aggregatedData[$formattedDate]['raw_index']    = $aggregatedData[$formattedDate]['row_count']*($percentile/100);
219
        }
220
221
        $return = [
222
            'ok'    => 1,
223
            'result'=> array_values($aggregatedData),
224
        ];
225
        return $return;
226
    }
227
228
229
    public function count(\Xhgui_Storage_Filter $filter) {
230
        list($query, $params) = $this->getQuery($filter, true);
231
        try {
232
            $stmt = $this->connection->prepare($query);
233
            $stmt->execute($params);
234
235
        } catch (\Exception $e) {
236
            print_r($e->getMessage());
237
            exit;
238
        }
239
240
        $ret = $stmt->fetch(\PDO::FETCH_ASSOC);
241
242
        if (!empty($ret['c'])) {
243
            return $ret['c'];
244
        }
245
        return 0;
246
    }
247
248
    public function findOne($id)
249
    {
250
        $stmt = $this->connection->prepare('
251
select 
252
    * 
253
from 
254
    profiles as p left join 
255
    profiles_info as i on (p.profile_id = i.id) LEFT JOIN
256
    profiles_meta as m on (p.profile_id = m.profile_id)
257
where 
258
    p.profile_id = :id
259
');
260
261
        $stmt->execute(['id'=>$id]);
262
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
263
        $row['profile'] = json_decode($row['profiles'], true);
264
        $row['meta']    = json_decode($row['meta'], true);
265
        $row['_id']     = $id;
266
267
        return $row;
268
    }
269
270
    public function remove($id)
271
    {
272
        $this->connection->beginTransaction();
273
        try {
274
            $profileStmt = $this->connection->prepare('delete from profiles where profile_id = :id');
275
            $profileStmt->execute(['id'=>$id]);
276
277
            $metaStmt = $this->connection->prepare('delete from profiles_meta where profile_id = :id');
278
            $metaStmt->execute(['id'=>$id]);
279
280
            $infoStmt = $this->connection->prepare('delete from profiles_info where id = :id');
281
            $infoStmt->execute(['id'=>$id]);
282
            
283
            $this->connection->commit();
284
        } catch (\Exception $e) {
285
            $this->connection->rollBack();
286
        }
287
    }
288
289
    /**
290
     *
291
     */
292
    public function drop()
293
    {
294
        $this->connection->exec('delete from profiles');
295
        $this->connection->exec('delete from profiles_meta');
296
        $this->connection->exec('delete from profiles_info');
297
    }
298
299
    public function getWatchedFunctions()
300
    {
301
        $stmt = $this->connection->query('select * from watched order by name desc');
302
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
303
    }
304
305
    public function addWatchedFunction($name)
306
    {
307
        $name = trim($name);
308
        if (empty($name)) {
309
            return false;
310
        }
311
        $stmt = $this->connection->prepare('INSERT INTO watched (name) VALUES (:name)');
312
        $stmt->execute(['name'=>trim($name)]);
313
        return true;
314
    }
315
316
    public function updateWatchedFunction($id, $name)
317
    {
318
        $stmt = $this->connection->prepare('update watched set name=:name where id = :id');
319
        $stmt->execute(['id'=>$id, 'name'=>$name]);
320
    }
321
322
    public function removeWatchedFunction($id)
323
    {
324
        $stmt = $this->connection->prepare('delete from watched where id = :id');
325
        $stmt->execute(['id'=>$id]);
326
    }
327
328
    /**
329
     * @param string|int $date
330
     * @return \DateTime
331
     */
332
    protected function getDateTimeFromString($date) {
333
334
        try {
335
            $datetime = \DateTime::createFromFormat('Y-m-d H:i:s', $date);
336
            if ($datetime instanceof \DateTime) {
337
                return $datetime;
338
            }
339
        } catch (\Exception $e){
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
340
        }
341
342
        try {
343
            $datetime = \DateTime::createFromFormat('U', $date);
344
            if ($datetime instanceof \DateTime) {
345
                return $datetime;
346
            }
347
        } catch (\Exception $e){
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
348
        }
349
350
        throw new \InvalidArgumentException('Unable to parse date');
351
    }
352
353
    public function insert(array $data) {
354
        // TODO: Implement insert() method.
355
    }
356
357
    public function update($_id, array $data) {
358
        // TODO: Implement update() method.
359
    }
360
}
361