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

Xhgui_Storage_PDO::getQuery()   F

Complexity

Conditions 26
Paths 9312

Size

Total Lines 144

Duplication

Lines 8
Ratio 5.56 %

Importance

Changes 0
Metric Value
dl 8
loc 144
rs 0
c 0
b 0
f 0
cc 26
nc 9312
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 extends Xhgui_Storage_Abstract implements
6
    \Xhgui_StorageInterface,
7
    \Xhgui_WatchedFunctionsStorageInterface
8
{
9
10
    /**
11
     * @var \PDO
12
     */
13
    protected $connection;
14
15
    /**
16
     * PDO constructor.
17
     * @param $config
18
     */
19
    public function __construct($config)
20
    {
21
        $this->connection = new \PDO(
22
            $config['db.dsn'],
23
            !empty($config['db.user'])      ? $config['db.user'] : null,
24
            !empty($config['db.password'])  ? $config['db.password'] : null,
25
            !empty($config['db.options'])   ? $config['db.options'] : []
26
        );
27
        $this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
28
        $this->connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
29
    }
30
31
    /**
32
     * @inheritDoc
33
     * @param \Xhgui_Storage_Filter $filter
34
     * @param bool $projections
35
     * @return \Xhgui_Storage_ResultSet
36
     */
37
    public function find(\Xhgui_Storage_Filter $filter, $projections = false)
38
    {
39
        list($query, $params) = $this->getQuery($filter, false);
40
        
41
        try {
42
            $stmt = $this->connection->prepare($query);
43
            $stmt->execute($params);
44
        } catch (\Exception $e) {
45
            print_r($e->getMessage());
46
            exit;
47
        }
48
49
        $tmp = [];
50
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
51
            $tmp[$row['id']] = $row;
52
            $tmp[$row['id']]['profile']    = json_decode($row['profiles'], true);
53
            $tmp[$row['id']]['meta']       = json_decode($row['meta'], true);
54
        }
55
        
56
        return new \Xhgui_Storage_ResultSet($tmp);
57
    }
58
59
    /**
60
     * Get query that is used for both list and count
61
     *
62
     * @param \Xhgui_Storage_Filter $filter
63
     * @param bool $count
64
     * @return array
65
     */
66
    protected function getQuery(\Xhgui_Storage_Filter $filter, $count = false)
67
    {
68
        $params = [];
69
70
        if ($count === true) {
71
            $columns = ' count(*) as c ';
72
        } else {
73
            $columns = ' p.*, i.*, m.*, p.profile_id as _id, main_wt as duration ';
74
        }
75
76
        $sql = "
77
select 
78
    $columns
79
from 
80
    profiles as p left join 
81
    profiles_info as i on (p.profile_id = i.id) LEFT JOIN
82
    profiles_meta as m on (p.profile_id = m.profile_id)
83
";
84
85
        $where = [];
86
87
        foreach ([
88
            'url'               => 'url',
89
            'method'            => 'method',
90
            'application'       => 'application',
91
            'version'           => 'version',
92
            'branch'            => 'branch',
93
            'controller'        => 'controller',
94
            'action'            => 'action',
95
            ] as $dbField => $field) {
96
            $method = 'get'.ucfirst($field);
97
98
            if ($filter->{$method}()) {
99
                switch($field) {
100
                    case 'url':
101
                        $url = $filter->{$method}();
102
                        $where[]              = ' ( url like :url OR simple_url like :simple_url)';
103
                        $params['url']        = '%'.$url.'%';
104
                        $params['simple_url'] = '%'.$url.'%';
105
                        break;
106
107
                    case 'action':
108 View Code Duplication
                    case 'controller':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
109
                        $where[]        = ' '.$dbField.' like :'.$field.' ';
110
                        $params[$field] = ($filter->{$method}()).'%';
111
                        break;
112
113 View Code Duplication
                    default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114
                        $where[]        = ' '.$dbField.' = :'.$field.' ';
115
                        $params[$field] = $filter->{$method}();
116
                        break;
117
                }
118
            }
119
        }
120
        
121
        if ($filter->getStartDate()) {
122
            $where[]                = ' request_time >= :startDate';
123
            $params['startDate']   = $this->getDateTimeFromString($filter->getStartDate(), 'start')
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...
124
                                          ->format('Y-m-d H:i:s');
125
        }
126
127
        if ($filter->getEndDate()) {
128
            $where[]                = ' request_time <= :endDate';
129
            $params['endDate']   = $this->getDateTimeFromString($filter->getEndDate(), 'end')
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...
130
                                        ->format('Y-m-d H:i:s');
131
        }
132
133
        if (!empty($where)) {
134
            $sql .= ' WHERE '.join(' AND ', $where);
135
        }
136
137
        if ($count === true) {
138
            return [$sql, $params];
139
        }
140
141
        switch ($filter->getSort()) {
142
            case 'ct':
143
                $sql .= ' order by main_ct';
144
                break;
145
146
            case 'wt':
147
                $sql .= ' order by main_wt';
148
                break;
149
150
            case 'cpu':
151
                $sql .= ' order by main_cpu';
152
                break;
153
154
            case 'mu':
155
                $sql .= ' order by main_mu';
156
                break;
157
158
            case 'pmu':
159
                $sql .= ' order by main_pmu';
160
                break;
161
162
            case 'controller':
163
                $sql .= ' order by controller';
164
                break;
165
166
            case 'action':
167
                $sql .= ' order by action';
168
                break;
169
170
            case 'application':
171
                $sql .= ' order by application';
172
                break;
173
174
            case 'branch':
175
                $sql .= ' order by branch';
176
                break;
177
178
            case 'version':
179
                $sql .= ' order by version';
180
                break;
181
182
            case 'time':
183
            default:
184
                $sql .= ' order by request_time';
185
                break;
186
        }
187
188
        switch ($filter->getDirection()) {
189
            case 'asc':
190
                $sql .= ' asc ';
191
                break;
192
193
            default:
194
            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...
195
                $sql .= ' desc ';
196
                break;
197
        }
198
199
        if ($filter->getPerPage()) {
200
            $sql            .= ' LIMIT :limit ';
201
            $params['limit'] = (int)$filter->getPerPage();
202
        }
203
204
        if ($filter->getPage()) {
205
            $sql                .= ' OFFSET :offset ';
206
            $params['offset']   = (int)($filter->getPerPage()*($filter->getPage()-1));
207
        }
208
        return [$sql, $params];
209
    }
210
211
    /**
212
     * @inheritDoc
213
     * @param Xhgui_Storage_Filter $filter
214
     * @param $col
215
     * @param int $percentile
216
     * @return array
217
     * @throws Exception
218
     */
219
    public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1)
220
    {
221
        $stmt = $this->connection->prepare('select 
222
    * 
223
from 
224
    profiles_info
225
where 
226
    simple_url = :simple_url OR url = :url
227
');
228
        $stmt->execute(['url'=> $filter->getUrl(), 'simple_url'=> $filter->getUrl()]);
229
        $aggregatedData = [];
230
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
231
            $date = new \DateTime($row['request_time']);
232
            $formattedDate = $date->format('Y-m-d H:i');
233 View Code Duplication
            if (empty($aggregatedData[$formattedDate])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234
                $aggregatedData[$formattedDate] = [
235
                    'wall_times'    => [],
236
                    'cpu_times'     => [],
237
                    'mu_times'      => [],
238
                    'pmu_times'     => [],
239
                    'row_count'     => 0
240
                ];
241
            }
242
243
            $aggregatedData[$formattedDate]['wall_times'][] = $row['main_wt'];
244
            $aggregatedData[$formattedDate]['cpu_times'][]  = $row['main_cpu'];
245
            $aggregatedData[$formattedDate]['mu_times'][]   = $row['main_mu'];
246
            $aggregatedData[$formattedDate]['pmu_times'][]  = $row['main_pmu'];
247
            $aggregatedData[$formattedDate]['row_count']++;
248
            $aggregatedData[$formattedDate]['_id']          = $date->format('Y-m-d H:i:s');
249
            $aggregatedData[$formattedDate]['raw_index']    =
250
                $aggregatedData[$formattedDate]['row_count']*($percentile/100);
251
        }
252
253
        $return = [
254
            'ok'    => 1,
255
            'result'=> array_values($aggregatedData),
256
        ];
257
        return $return;
258
    }
259
260
    /**
261
     * @inheritDoc
262
     * @@param Xhgui_Storage_Filter $filter
263
     * @return int
264
     */
265
    public function count(\Xhgui_Storage_Filter $filter)
266
    {
267
        list($query, $params) = $this->getQuery($filter, true);
268
        try {
269
            $stmt = $this->connection->prepare($query);
270
            $stmt->execute($params);
271
        } catch (\Exception $e) {
272
            print_r($e->getMessage());
273
            exit;
274
        }
275
276
        $ret = $stmt->fetch(\PDO::FETCH_ASSOC);
277
278
        if (!empty($ret['c'])) {
279
            return $ret['c'];
280
        }
281
        return 0;
282
    }
283
284
    /**
285
     * @inheritDoc
286
     * @param $id
287
     * @return mixed
288
     */
289
    public function findOne($id)
290
    {
291
        $stmt = $this->connection->prepare('
292
select 
293
    * 
294
from 
295
    profiles as p left join 
296
    profiles_info as i on (p.profile_id = i.id) LEFT JOIN
297
    profiles_meta as m on (p.profile_id = m.profile_id)
298
where 
299
    p.profile_id = :id
300
');
301
302
        $stmt->execute(['id'=>$id]);
303
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
304
        $row['profile'] = json_decode($row['profiles'], true);
305
        $row['meta']    = json_decode($row['meta'], true);
306
        $row['_id']     = $id;
307
308
        return $row;
309
    }
310
311
    /**
312
     * @inheritDoc
313
     * @param $id
314
     */
315
    public function remove($id)
316
    {
317
        $this->connection->beginTransaction();
318
        try {
319
            $profileStmt = $this->connection->prepare('delete from profiles where profile_id = :id');
320
            $profileStmt->execute(['id'=>$id]);
321
322
            $metaStmt = $this->connection->prepare('delete from profiles_meta where profile_id = :id');
323
            $metaStmt->execute(['id'=>$id]);
324
325
            $infoStmt = $this->connection->prepare('delete from profiles_info where id = :id');
326
            $infoStmt->execute(['id'=>$id]);
327
            
328
            $this->connection->commit();
329
        } catch (\Exception $e) {
330
            $this->connection->rollBack();
331
        }
332
    }
333
334
    /**
335
     * @inheritDoc
336
     * Remove all data from profile tables
337
     */
338
    public function drop()
339
    {
340
        $this->connection->exec('delete from profiles');
341
        $this->connection->exec('delete from profiles_meta');
342
        $this->connection->exec('delete from profiles_info');
343
    }
344
345
    /**
346
     * @inheritDoc
347
     * @return array
348
     */
349
    public function getWatchedFunctions()
350
    {
351
        $stmt = $this->connection->query('select * from watched order by name desc');
352
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
353
    }
354
355
    /**
356
     * @inheritDoc
357
     * @param $name
358
     * @return bool
359
     */
360
    public function addWatchedFunction($name)
361
    {
362
        $name = trim($name);
363
        if (empty($name)) {
364
            return false;
365
        }
366
        $stmt = $this->connection->prepare('INSERT INTO watched (name) VALUES (:name)');
367
        $stmt->execute(['name'=>trim($name)]);
368
        return true;
369
    }
370
371
    /**
372
     * @inheritDoc
373
     * @param $id
374
     * @param $name
375
     */
376
    public function updateWatchedFunction($id, $name)
377
    {
378
        $stmt = $this->connection->prepare('update watched set name=:name where id = :id');
379
        $stmt->execute(['id'=>$id, 'name'=>$name]);
380
    }
381
382
    /**
383
     * @inheritDoc
384
     * @param $id
385
     */
386
    public function removeWatchedFunction($id)
387
    {
388
        $stmt = $this->connection->prepare('delete from watched where id = :id');
389
        $stmt->execute(['id'=>$id]);
390
    }
391
}
392