Completed
Pull Request — master (#268)
by
unknown
03:49 queued 02:35
created

Xhgui_Storage_Mongo::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 2
nc 2
nop 2
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($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, $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) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
245
        }
246
        return $ret;
247
    }
248
249
    /**
250
     * @param $name
251
     * @return bool
252
     */
253 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...
254
    {
255
256
        $name = trim($name);
257
        if (empty($name)) {
258
            return false;
259
        }
260
261
        try {
262
            $id = new \MongoId();
263
264
            $data = [
265
                '_id'   => $id,
266
                'name'  => $name
267
            ];
268
            $this->connection->watches->insert($data);
269
270
            return true;
271
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
272
        }
273
        return false;
274
    }
275
276
    /**
277
     * @param $id
278
     * @param $name
279
     * @return bool
280
     */
281 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...
282
    {
283
        $name = trim($name);
284
        if (empty($name)) {
285
            return false;
286
        }
287
288
        try {
289
            $id = new \MongoId($id);
290
            $data = [
291
                '_id'   => $id,
292
                'name'  => $name
293
            ];
294
            $this->connection->watches->save($data);
295
296
            return true;
297
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
298
        }
299
300
        return false;
301
    }
302
303
    /**
304
     * @param $id
305
     * @return bool
306
     */
307
    public function removeWatchedFunction($id)
308
    {
309
310
        try {
311
            $id = new \MongoId($id);
312
313
            $this->connection->watches->remove(['_id'=>$id]);
314
315
            return true;
316
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
317
        }
318
        return false;
319
    }
320
321
    /**
322
     * @param \Xhgui_Storage_Filter $options
0 ignored issues
show
Bug introduced by
There is no parameter named $options. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
323
     * @return \DateTime
324
     */
325
    protected function getDateTimeFromString($date)
326
    {
327
328
        try {
329
            return \DateTime::createFromFormat('Y-m-d H:i:s', $date);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression \DateTime::createFromFor...('Y-m-d H:i:s', $date); of type DateTime|false adds false to the return on line 329 which is incompatible with the return type documented by Xhgui_Storage_Mongo::getDateTimeFromString of type DateTime. It seems like you forgot to handle an error condition.
Loading history...
330
        } catch (\Exception $e){
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
331
        }
332
333
        try {
334
            return \DateTime::createFromFormat('U', $date);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression \DateTime::createFromFormat('U', $date); of type DateTime|false adds false to the return on line 334 which is incompatible with the return type documented by Xhgui_Storage_Mongo::getDateTimeFromString of type DateTime. It seems like you forgot to handle an error condition.
Loading history...
335
        } catch (\Exception $e){
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
336
        }
337
338
        throw new \InvalidArgumentException('Unable to parse date');
339
    }
340
341
    /**
342
     * @param \Xhgui_Storage_Filter $filter
343
     * @return array
344
     */
345
    protected function getConditions(\Xhgui_Storage_Filter $filter)
346
    {
347
        $conditions = [];
348 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...
349
            $conditions['meta.request_ts']['$gte'] = new \MongoDate($this->getDateTimeFromString($filter->getStartDate())
350
                                                                         ->format('U'));
351
        }
352
353 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...
354
            $conditions['meta.request_ts']['$lte'] = new \MongoDate($this->getDateTimeFromString($filter->getEndDate())
355
                                                                         ->format('U'));
356
357
        }
358
359
        if (null !== $filter->getUrl()) {
360
            $conditions['meta.simple_url'] = $filter->getUrl();
361
        }
362
363
        foreach ([
364
                     'method'      => 'method',
365
                     'application' => 'application',
366
                     'version'     => 'version',
367
                     'branch'      => 'branch',
368
                     'controller'  => 'controller',
369
                     'action'      => 'action',
370
                 ] as $dbField => $field) {
371
            $method = 'get' . ucfirst($field);
372
            if ($filter->{$method}()) {
373
                $conditions['meta.' . $dbField] = $filter->get{$method}();
0 ignored issues
show
Bug introduced by
The property get does not seem to exist in Xhgui_Storage_Filter.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
374
            }
375
        }
376
377
        return $conditions;
378
    }
379
}
380