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

Xhgui_Storage_Mongo::remove()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
dl 7
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Class Xhgui_Storage_Mongo
5
 */
6
class Xhgui_Storage_Mongo implements \Xhgui_StorageInterface, \Xhgui_WatchedFunctionsStorageInterface
7
{
8
9
    /**
10
     * @var \MongoDB
11
     */
12
    protected $connection;
13
14
    /**
15
     * @var int
16
     */
17
    protected $defaultPerPage = 25;
18
19
    /**
20
     * @var string
21
     */
22
    protected $collection;
23
    /**
24
     * @var \MongoClient
25
     */
26
    protected $mongoClient;
27
28
    /**
29
     * Mongo constructor.
30
     * @param $config
31
     * @throws \MongoConnectionException
32
     * @throws \MongoException
33
     */
34
    public function __construct($config, $collection = 'results')
35
    {
36
37
        // set default number of rows for all results. This can be changed
38
        // for each query
39
        $this->defaultPerPage = $config['page.limit'];
40
41
        $this->collection = $collection;
42
43
        // make sure options is an array
44
        if (empty($config['db.options'])) {
45
            $config['db.options'] = array();
46
        }
47
48
        $config['db.options']['connect'] = true;
49
50
        // create client
51
        $this->mongoClient = new \MongoClient($config['db.host'], $config['db.options']);
52
53
        $this->connection = $this->mongoClient->{$config['db.db']};
54
    }
55
56
    /**
57
     * @param $options
58
     * @param bool $projections
59
     * @return Xhgui_Storage_ResultSet
60
     * @throws \MongoCursorException
61
     */
62
    public function find(\Xhgui_Storage_Filter $filter, $projections = false)
63
    {
64
        $sort = [];
65
        switch ($filter->getSort()) {
66
            case 'ct':
67
            case 'wt':
68
            case 'cpu':
69
            case 'mu':
70
            case 'pmu':
71
                $sort['profile.main().'.$filter->getSort()] = $filter->getDirection() === 'asc' ? 1 : -1;
72
                break;
73
            case 'time':
74
                $sort['meta.request_ts'] = $filter->getDirection() === 'asc' ? 1 : -1;
75
                break;
76
        }
77
78
        $conditions = $this->getConditions($filter);
79
80
        $ret = $this->connection->{$this->collection}
81
            ->find($conditions)
82
            ->sort($sort)
83
            ->skip((int)($filter->getPage() - 1) * $filter->getPerPage())
84
            ->limit($filter->getPerPage());
85
86
87
        $result = new \Xhgui_Storage_ResultSet(iterator_to_array($ret));
88
        return $result;
89
    }
90
91
    /**
92
     * @param $options
93
     * @return int
94
     * @throws \MongoCursorTimeoutException
95
     * @throws \MongoException
96
     */
97
    public function count(\Xhgui_Storage_Filter $filter)
98
    {
99
        $conditions = $this->getConditions($filter);
100
101
        $ret = $this->connection->{$this->collection}->find($conditions,  array('_id' => 1))->count();
102
        return $ret;
103
    }
104
105
    /**
106
     * @param $id
107
     * @return array|null
108
     * @throws \MongoException
109
     */
110
    public function findOne($id)
111
    {
112
        $ret = $this->connection->{$this->collection}
113
            ->findOne(['_id'=>new \MongoId($id)]);
114
        return $ret;
115
116
    }
117
118
    /**
119
     * @param $id
120
     * @return array|bool
121
     * @throws MongoCursorException
122
     * @throws MongoCursorTimeoutException
123
     * @throws MongoException
124
     */
125 View Code Duplication
    public function remove($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
126
    {
127
        return $this->connection->{$this->collection}->remove(
128
            array('_id' => new MongoId($id)),
129
            array('w' => 1)
130
        );
131
    }
132
133
    /**
134
     * @param $data
135
     * @return array|bool
136
     * @throws \MongoCursorException
137
     * @throws \MongoCursorTimeoutException
138
     * @throws \MongoException
139
     */
140
    public function insert(array $data)
141
    {
142
        return $this->connection->{$this->collection}->insert(
143
            $data,
144
            array('w' => 1)
145
        );
146
    }
147
148
    /**
149
     * @param $id
150
     * @param $data
151
     * @return array|bool
152
     * @throws \MongoCursorException
153
     * @throws \MongoException
154
     * @throws \MongoWriteConcernException
155
     */
156 View Code Duplication
    public function update($id, array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
157
    {
158
        return $this->connection->{$this->collection}->update(
159
            array('_id' => new MongoId($id)),
160
            $data,
161
            array('w' => 1)
162
        );
163
    }
164
165
    /**
166
     *
167
     */
168
    public function drop()
169
    {
170
        // TODO: Implement drop() method.
171
    }
172
173
    /**
174
     * @param $match
175
     * @param $col
176
     * @param int $percentile
177
     * @return array
178
     * @throws \MongoException
179
     */
180
    public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1)
181
    {
182
183
        $conditions = $this->getConditions($filter);
184
        $param = [
185
            ['$match' => $conditions],
186
            [
187
                '$project' => [
188
                    'date' => '$meta.request_ts',
189
                    'profile.main()' => 1
190
                ]
191
            ],
192
            [
193
                '$group' => [
194
                    '_id'           => '$date',
195
                    'row_count'     => ['$sum'  => 1],
196
                    'wall_times'    => ['$push' => '$profile.main().wt'],
197
                    'cpu_times'     => ['$push' => '$profile.main().cpu'],
198
                    'mu_times'      => ['$push' => '$profile.main().mu'],
199
                    'pmu_times'     => ['$push' => '$profile.main().pmu'],
200
                ]
201
            ],
202
            [
203
                '$project' => [
204
                    'date' => '$date',
205
                    'row_count' => '$row_count',
206
                    'raw_index' => [
207
                        '$multiply' => [
208
                            '$row_count',
209
                            $percentile / 100
210
                        ]
211
                    ],
212
                    'wall_times'    => '$wall_times',
213
                    'cpu_times'     => '$cpu_times',
214
                    'mu_times'      => '$mu_times',
215
                    'pmu_times'     => '$pmu_times',
216
                ]
217
            ],
218
            [
219
                '$sort' => ['_id' => 1]
220
            ],
221
        ];
222
        $ret = $this->connection->{$this->collection}->aggregate(
223
            $param,
224
            [
225
                'cursor' => ['batchSize' => 0]
226
            ]
227
        );
228
229
        return $ret;
230
    }
231
232
    /**
233
     * @return array
234
     */
235
    public function getWatchedFunctions()
236
    {
237
        $ret = [];
238
        try{
239
            $cursor = $this->connection->watches->find()->sort(['name'=>1]);
240
            $ret = [];
241
            foreach($cursor as $row) {
242
                $ret[] = ['id'=>$row['_id']->__toString(), 'name'=>$row['name']];
243
            }
244
        } catch (\Exception $e) {
245
            // if something goes wrong just return empty array
246
            // @todo add exception
247
        }
248
        return $ret;
249
    }
250
251
    /**
252
     * @param $name
253
     * @return bool
254
     */
255 View Code Duplication
    public function addWatchedFunction($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
256
    {
257
258
        $name = trim($name);
259
        if (empty($name)) {
260
            return false;
261
        }
262
263
        try {
264
            $id = new \MongoId();
265
266
            $data = [
267
                '_id'   => $id,
268
                'name'  => $name
269
            ];
270
            $this->connection->watches->insert($data);
271
272
            return true;
273
        } catch (\Exception $e) {
274
            // if something goes wrong just ignore for now
275
            // @todo add exception
276
        }
277
        return false;
278
    }
279
280
    /**
281
     * @param $id
282
     * @param $name
283
     * @return bool
284
     */
285 View Code Duplication
    public function updateWatchedFunction($id, $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
286
    {
287
        $name = trim($name);
288
        if (empty($name)) {
289
            return false;
290
        }
291
292
        try {
293
            $id = new \MongoId($id);
294
            $data = [
295
                '_id'   => $id,
296
                'name'  => $name
297
            ];
298
            $this->connection->watches->save($data);
299
300
            return true;
301
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
302
        }
303
304
        return false;
305
    }
306
307
    /**
308
     * @param $id
309
     * @return bool
310
     */
311
    public function removeWatchedFunction($id)
312
    {
313
314
        try {
315
            $id = new \MongoId($id);
316
317
            $this->connection->watches->remove(['_id'=>$id]);
318
319
            return true;
320
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
321
        }
322
        return false;
323
    }
324
325
    /**
326
     * @param string|int $date
327
     * @return \DateTime
328
     */
329
    protected function getDateTimeFromString($date)
330
    {
331
332
        try {
333
            $parsedDate = \DateTime::createFromFormat('Y-m-d H:i:s', $date);
334
            if (!empty($parsedDate)) {
335
                return $parsedDate;
336
            }
337
338
        } catch (\Exception $e){
339
            // leave empty to try parse different format below
340
        }
341
342
        try {
343
            $parsedDate = \DateTime::createFromFormat('U', $date);
344
            if (!empty($parsedDate)) {
345
                return $parsedDate;
346
            }
347
348
        } catch (\Exception $e){
349
            // throw generic exception on failure
350
        }
351
        throw new \InvalidArgumentException('Unable to parse date');
352
    }
353
354
    /**
355
     * @param \Xhgui_Storage_Filter $filter
356
     * @return array
357
     */
358
    protected function getConditions(\Xhgui_Storage_Filter $filter)
359
    {
360
        $conditions = [];
361 View Code Duplication
        if (null !== $filter->getStartDate()) {
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...
362
            $conditions['meta.request_ts']['$gte'] = new \MongoDate($this->getDateTimeFromString($filter->getStartDate())
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...
363
                                                                         ->format('U'));
364
        }
365
366 View Code Duplication
        if (null !== $filter->getEndDate()) {
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...
367
            $conditions['meta.request_ts']['$lte'] = new \MongoDate($this->getDateTimeFromString($filter->getEndDate())
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...
368
                                                                         ->format('U'));
369
370
        }
371
372
        if (null !== $filter->getUrl()) {
373
            $conditions['meta.simple_url'] = $filter->getUrl();
374
        }
375
376
        foreach ([
377
                     'method'      => 'method',
378
                     'application' => 'application',
379
                     'version'     => 'version',
380
                     'branch'      => 'branch',
381
                     'controller'  => 'controller',
382
                     'action'      => 'action',
383
                 ] as $dbField => $field) {
384
            $method = 'get' . ucfirst($field);
385
            if ($filter->{$method}()) {
386
                $conditions['meta.' . $dbField] = $filter->{$method}();
387
            }
388
        }
389
390
        return $conditions;
391
    }
392
}
393