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

Xhgui_Storage_PDO::getQuery()   F

Complexity

Conditions 23
Paths 4656

Size

Total Lines 125

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 125
rs 0
c 0
b 0
f 0
cc 23
nc 4656
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Get profiles using PDO database connection
4
 */
5
class Xhgui_Storage_PDO implements \Xhgui_StorageInterface, \Xhgui_WatchedFunctionsStorageInterface
6
{
7
8
    /**
9
     * @var \PDO
10
     */
11
    protected $connection;
12
13
    /**
14
     * PDO constructor.
15
     * @param $config
16
     */
17
    public function __construct($config)
18
    {
19
        $this->connection = new \PDO(
20
            $config['db.dsn'],
21
            !empty($config['db.user'])      ? $config['db.user'] : null,
22
            !empty($config['db.password'])  ? $config['db.password'] : null,
23
            !empty($config['db.options'])   ? $config['db.options'] : []
24
        );
25
        $this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
26
        $this->connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
27
    }
28
29
    /**
30
     * @inheritDoc
31
     * @param \Xhgui_Storage_Filter $filter
32
     * @param bool $projections
33
     * @return \Xhgui_Storage_ResultSet
34
     */
35
    public function find(\Xhgui_Storage_Filter $filter, $projections = false)
36
    {
37
        list($query, $params) = $this->getQuery($filter, false);
38
        
39
        try {
40
            $stmt = $this->connection->prepare($query);
41
            $stmt->execute($params);
42
        } catch (\Exception $e) {
43
            print_r($e->getMessage());
44
            exit;
45
        }
46
47
        $tmp = [];
48
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
49
            $tmp[$row['id']] = $row;
50
            $tmp[$row['id']]['profile']    = json_decode($row['profiles'], true);
51
            $tmp[$row['id']]['meta']       = json_decode($row['meta'], true);
52
        }
53
        
54
        return new \Xhgui_Storage_ResultSet($tmp);
55
    }
56
57
    /**
58
     * Get query that is used for both list and count
59
     *
60
     * @param \Xhgui_Storage_Filter $filter
61
     * @param bool $count
62
     * @return array
63
     */
64
    protected function getQuery(\Xhgui_Storage_Filter $filter, $count = false)
65
    {
66
        $params = [];
67
68
        if ($count === true) {
69
            $columns = ' count(*) as c ';
70
        } else {
71
            $columns = ' p.*, i.*, m.*, p.profile_id as _id, main_wt as duration ';
72
        }
73
74
        $sql = "
75
select 
76
    $columns
77
from 
78
    profiles as p left join 
79
    profiles_info as i on (p.profile_id = i.id) LEFT JOIN
80
    profiles_meta as m on (p.profile_id = m.profile_id)
81
";
82
83
        $where = [];
84
85
        foreach ([
86
            'url'               => 'url',
87
            'method'            => 'method',
88
            'application'       => 'application',
89
            'version'           => 'version',
90
            'branch'            => 'branch',
91
            'controller'        => 'controller',
92
            'action'            => 'action',
93
            ] as $dbField => $field) {
94
            $method = 'get'.ucfirst($field);
95
96
            if ($filter->{$method}()) {
97
                $where[]        = ' '.$dbField.' = :'.$field.' ';
98
                $params[$field]  = $filter->{$method}();
99
            }
100
        }
101
        
102
        if ($filter->getStartDate()) {
103
            $where[]                = ' request_time >= :startDate';
104
            $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...
105
        }
106
107
        if ($filter->getEndDate()) {
108
            $where[]                = ' request_time <= :endDate';
109
            $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...
110
        }
111
112
        if (!empty($where)) {
113
            $sql .= ' WHERE '.join(' AND ', $where);
114
        }
115
116
        if ($count === true) {
117
            return [$sql, $params];
118
        }
119
120
        switch ($filter->getSort()) {
121
            case 'ct':
122
                $sql .= ' order by main_ct';
123
                break;
124
125
            case 'wt':
126
                $sql .= ' order by main_wt';
127
                break;
128
129
            case 'cpu':
130
                $sql .= ' order by main_cpu';
131
                break;
132
133
            case 'mu':
134
                $sql .= ' order by main_mu';
135
                break;
136
137
            case 'pmu':
138
                $sql .= ' order by main_pmu';
139
                break;
140
141
            case 'controller':
142
                $sql .= ' order by controller';
143
                break;
144
145
            case 'action':
146
                $sql .= ' order by action';
147
                break;
148
149
            case 'application':
150
                $sql .= ' order by application';
151
                break;
152
153
            case 'branch':
154
                $sql .= ' order by branch';
155
                break;
156
157
            case 'version':
158
                $sql .= ' order by version';
159
                break;
160
161
            case 'time':
162
            default:
163
                $sql .= ' order by request_time';
164
                break;
165
        }
166
167
        switch ($filter->getDirection()) {
168
            case 'asc':
169
                $sql .= ' asc ';
170
                break;
171
172
            default:
173
            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...
174
                $sql .= ' desc ';
175
                break;
176
        }
177
178
        if ($filter->getPerPage()) {
179
            $sql            .= ' LIMIT :limit ';
180
            $params['limit'] = (int)$filter->getPerPage();
181
        }
182
183
        if ($filter->getPage()) {
184
            $sql                .= ' OFFSET :offset ';
185
            $params['offset']   = (int)($filter->getPerPage()*($filter->getPage()-1));
186
        }
187
        return [$sql, $params];
188
    }
189
190
    /**
191
     * @inheritDoc
192
     * @param Xhgui_Storage_Filter $filter
193
     * @param $col
194
     * @param int $percentile
195
     * @return array
196
     * @throws Exception
197
     */
198
    public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1)
199
    {
200
        $stmt = $this->connection->prepare('select 
201
    * 
202
from 
203
    profiles_info
204
where 
205
    url = :url
206
');
207
        $stmt->execute(['url'=> $filter->getUrl()]);
208
        $aggregatedData = [];
209
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
210
            $date = new \DateTime($row['request_time']);
211
            $formattedDate = $date->format('Y-m-d');
212
            if (empty($aggregatedData[$date->format('Y-m-d')])) {
213
                $aggregatedData[$date->format('Y-m-d')] = [
214
                    'wall_times'    => [],
215
                    'cpu_times'     => [],
216
                    'mu_times'      => [],
217
                    'pmu_times'     => [],
218
                    'row_count'     => 0
219
                ];
220
            }
221
222
            $aggregatedData[$formattedDate]['wall_times'][] = $row['main_wt'];
223
            $aggregatedData[$formattedDate]['cpu_times'][]  = $row['main_ct'];
224
            $aggregatedData[$formattedDate]['mu_times'][]   = $row['main_mu'];
225
            $aggregatedData[$formattedDate]['pmu_times'][]  = $row['main_pmu'];
226
            $aggregatedData[$formattedDate]['row_count']++;
227
            $aggregatedData[$formattedDate]['_id']          = $formattedDate;
228
            $aggregatedData[$formattedDate]['raw_index']    =
229
                $aggregatedData[$formattedDate]['row_count']*($percentile/100);
230
        }
231
232
        $return = [
233
            'ok'    => 1,
234
            'result'=> array_values($aggregatedData),
235
        ];
236
        return $return;
237
    }
238
239
    /**
240
     * @inheritDoc
241
     * @@param Xhgui_Storage_Filter $filter
242
     * @return int
243
     */
244
    public function count(\Xhgui_Storage_Filter $filter)
245
    {
246
        list($query, $params) = $this->getQuery($filter, true);
247
        try {
248
            $stmt = $this->connection->prepare($query);
249
            $stmt->execute($params);
250
        } catch (\Exception $e) {
251
            print_r($e->getMessage());
252
            exit;
253
        }
254
255
        $ret = $stmt->fetch(\PDO::FETCH_ASSOC);
256
257
        if (!empty($ret['c'])) {
258
            return $ret['c'];
259
        }
260
        return 0;
261
    }
262
263
    /**
264
     * @inheritDoc
265
     * @param $id
266
     * @return mixed
267
     */
268
    public function findOne($id)
269
    {
270
        $stmt = $this->connection->prepare('
271
select 
272
    * 
273
from 
274
    profiles as p left join 
275
    profiles_info as i on (p.profile_id = i.id) LEFT JOIN
276
    profiles_meta as m on (p.profile_id = m.profile_id)
277
where 
278
    p.profile_id = :id
279
');
280
281
        $stmt->execute(['id'=>$id]);
282
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
283
        $row['profile'] = json_decode($row['profiles'], true);
284
        $row['meta']    = json_decode($row['meta'], true);
285
        $row['_id']     = $id;
286
287
        return $row;
288
    }
289
290
    /**
291
     * @inheritDoc
292
     * @param $id
293
     */
294
    public function remove($id)
295
    {
296
        $this->connection->beginTransaction();
297
        try {
298
            $profileStmt = $this->connection->prepare('delete from profiles where profile_id = :id');
299
            $profileStmt->execute(['id'=>$id]);
300
301
            $metaStmt = $this->connection->prepare('delete from profiles_meta where profile_id = :id');
302
            $metaStmt->execute(['id'=>$id]);
303
304
            $infoStmt = $this->connection->prepare('delete from profiles_info where id = :id');
305
            $infoStmt->execute(['id'=>$id]);
306
            
307
            $this->connection->commit();
308
        } catch (\Exception $e) {
309
            $this->connection->rollBack();
310
        }
311
    }
312
313
    /**
314
     * @inheritDoc
315
     * Remove all data from profile tables
316
     */
317
    public function drop()
318
    {
319
        $this->connection->exec('delete from profiles');
320
        $this->connection->exec('delete from profiles_meta');
321
        $this->connection->exec('delete from profiles_info');
322
    }
323
324
    /**
325
     * @inheritDoc
326
     * @return array
327
     */
328
    public function getWatchedFunctions()
329
    {
330
        $stmt = $this->connection->query('select * from watched order by name desc');
331
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
332
    }
333
334
    /**
335
     * @inheritDoc
336
     * @param $name
337
     * @return bool
338
     */
339
    public function addWatchedFunction($name)
340
    {
341
        $name = trim($name);
342
        if (empty($name)) {
343
            return false;
344
        }
345
        $stmt = $this->connection->prepare('INSERT INTO watched (name) VALUES (:name)');
346
        $stmt->execute(['name'=>trim($name)]);
347
        return true;
348
    }
349
350
    /**
351
     * @inheritDoc
352
     * @param $id
353
     * @param $name
354
     */
355
    public function updateWatchedFunction($id, $name)
356
    {
357
        $stmt = $this->connection->prepare('update watched set name=:name where id = :id');
358
        $stmt->execute(['id'=>$id, 'name'=>$name]);
359
    }
360
361
    /**
362
     * @inheritDoc
363
     * @param $id
364
     */
365
    public function removeWatchedFunction($id)
366
    {
367
        $stmt = $this->connection->prepare('delete from watched where id = :id');
368
        $stmt->execute(['id'=>$id]);
369
    }
370
371
    /**
372
     * Try to get date from Y-m-d H:i:s or from timestamp
373
     *
374
     * @param string|int $date
375
     * @return \DateTime
376
     */
377
    protected function getDateTimeFromString($date)
378
    {
379
380
        try {
381
            $datetime = \DateTime::createFromFormat('Y-m-d H:i:s', $date);
382
            if ($datetime instanceof \DateTime) {
383
                return $datetime;
384
            }
385
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
386
        }
387
388
        try {
389
            $datetime = \DateTime::createFromFormat('U', $date);
390
            if ($datetime instanceof \DateTime) {
391
                return $datetime;
392
            }
393
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
394
        }
395
396
        throw new \InvalidArgumentException('Unable to parse date');
397
    }
398
399
    /**
400
     * @inheritDoc
401
     * @param array $data
402
     */
403
    public function insert(array $data)
404
    {
405
        // TODO: Implement insert() method.
406
    }
407
408
    /**
409
     * @inheritDoc
410
     * @param $_id
411
     * @param array $data
412
     */
413
    public function update($_id, array $data)
414
    {
415
        // TODO: Implement update() method.
416
    }
417
}
418