Completed
Push — master ( 022281...f9cc19 )
by Elan
20s
created

src/Xhgui/Profiles.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Contains logic for getting/creating/removing profile records.
4
 */
5
class Xhgui_Profiles
6
{
7
    protected $_collection;
8
9
    protected $_mapper;
10
11
    public function __construct(MongoDb $db)
12
    {
13
        $this->_collection = $db->results;
14
        $this->_mapper = new Xhgui_Db_Mapper();
15
    }
16
17
    /**
18
     * Get the latest profile data.
19
     *
20
     * @return Xhgui_Profile
21
     */
22
    public function latest()
23
    {
24
        $cursor = $this->_collection->find()
25
            ->sort(array('meta.request_date' => -1))
26
            ->limit(1);
27
        $result = $cursor->getNext();
28
        return $this->_wrap($result);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->_wrap($result); of type Xhgui_Profile|array adds the type array to the return on line 28 which is incompatible with the return type documented by Xhgui_Profiles::latest of type Xhgui_Profile.
Loading history...
29
    }
30
31
    public function query($conditions, $fields = null)
32
    {
33
        return $this->_collection->find($conditions, $fields);
34
    }
35
36
    /**
37
     * Get a single profile run by id.
38
     *
39
     * @param string $id The id of the profile to get.
40
     * @return Xhgui_Profile
41
     */
42
    public function get($id)
43
    {
44
        return $this->_wrap($this->_collection->findOne(array(
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->_wrap($this->_col...> new \MongoId($id)))); of type Xhgui_Profile|array adds the type array to the return on line 44 which is incompatible with the return type documented by Xhgui_Profiles::get of type Xhgui_Profile.
Loading history...
45
            '_id' => new MongoId($id)
46
        )));
47
    }
48
49
    /**
50
     * Get the list of profiles for a simplified url.
51
     *
52
     * @param string $url The url to load profiles for.
53
     * @param array $options Pagination options to use.
54
     * @param array $conditions The search options.
55
     * @return MongoCursor
56
     */
57
    public function getForUrl($url, $options, $conditions = array())
58
    {
59
        $conditions = array_merge(
60
            (array)$conditions,
61
            array('simple_url' => $url)
62
        );
63
        $options = array_merge($options, array(
64
            'conditions' => $conditions,
65
        ));
66
        return $this->paginate($options);
67
    }
68
69
    public function paginate($options)
70
    {
71
        $opts = $this->_mapper->convert($options);
72
73
        $totalRows = $this->_collection->find(
74
            $opts['conditions'],
75
            array('_id' => 1))->count();
76
77
        $totalPages = max(ceil($totalRows / $opts['perPage']), 1);
78
        $page = 1;
79
        if (isset($options['page'])) {
80
            $page = min(max($options['page'], 1), $totalPages);
81
        }
82
83
        $projection = false;
84
        if (isset($options['projection'])) {
85
            if ($options['projection'] === true) {
86
                $projection = array('meta' => 1, 'profile.main()' => 1);
87
            } else {
88
                $projection = $options['projection'];
89
            }
90
        }
91
92
        if ($projection === false) {
93
            $cursor = $this->_collection->find($opts['conditions'])
94
                ->sort($opts['sort'])
95
                ->skip((int)($page - 1) * $opts['perPage'])
96
                ->limit($opts['perPage']);
97
        } else {
98
            $cursor = $this->_collection->find($opts['conditions'], $projection)
99
                ->sort($opts['sort'])
100
                ->skip((int)($page - 1) * $opts['perPage'])
101
                ->limit($opts['perPage']);
102
        }
103
104
        return array(
105
            'results' => $this->_wrap($cursor),
106
            'sort' => $opts['sort'],
107
            'direction' => $opts['direction'],
108
            'page' => $page,
109
            'perPage' => $opts['perPage'],
110
            'totalPages' => $totalPages
111
        );
112
    }
113
114
    /**
115
     * Get the Percentile metrics for a URL
116
     *
117
     * This will group data by date and returns only the
118
     * percentile + date, making the data ideal for time series graphs
119
     *
120
     * @param integer $percentile The percentile you want. e.g. 90.
121
     * @param string $url
122
     * @param array $search Search options containing date_start and or date_end
123
     * @return array Array of metrics grouped by date
124
     */
125
    public function getPercentileForUrl($percentile, $url, $search = array())
126
    {
127
        $result = $this->_mapper->convert(array(
128
            'conditions' => $search + array('simple_url' => $url)
129
        ));
130
        $match = $result['conditions'];
131
132
        $col = '$meta.request_date';
133 View Code Duplication
        if (!empty($search['limit']) && $search['limit'][0] == "P") {
134
            $col = '$meta.request_ts';
135
        }
136
137
        $results = $this->_collection->aggregate(array(
138
            array('$match' => $match),
139
            array(
140
                '$project' => array(
141
                    'date' => $col,
142
                    'profile.main()' => 1
143
                )
144
            ),
145
            array(
146
                '$group' => array(
147
                    '_id' => '$date',
148
                    'row_count' => array('$sum' => 1),
149
                    'wall_times' => array('$push' => '$profile.main().wt'),
150
                    'cpu_times' => array('$push' => '$profile.main().cpu'),
151
                    'mu_times' => array('$push' => '$profile.main().mu'),
152
                    'pmu_times' => array('$push' => '$profile.main().pmu'),
153
                )
154
            ),
155
            array(
156
                '$project' => array(
157
                    'date' => '$date',
158
                    'row_count' => '$row_count',
159
                    'raw_index' => array(
160
                        '$multiply' => array(
161
                            '$row_count',
162
                            $percentile / 100
163
                        )
164
                    ),
165
                    'wall_times' => '$wall_times',
166
                    'cpu_times' => '$cpu_times',
167
                    'mu_times' => '$mu_times',
168
                    'pmu_times' => '$pmu_times',
169
                )
170
            ),
171
            array('$sort' => array('_id' => 1)),
172
            ),
173
            array('cursor' => array('batchSize' => 0))
174
        );
175
176
        if (empty($results['result'])) {
177
            return array();
178
        }
179
        $keys = array(
180
            'wall_times' => 'wt',
181
            'cpu_times' => 'cpu',
182
            'mu_times' => 'mu',
183
            'pmu_times' => 'pmu'
184
        );
185
        foreach ($results['result'] as &$result) {
186
            $result['date'] = ($result['_id'] instanceof MongoDate) ? date('Y-m-d H:i:s', $result['_id']->sec) : $result['_id'];
187
            unset($result['_id']);
188
            $index = max(round($result['raw_index']) - 1, 0);
189
            foreach ($keys as $key => $out) {
190
                sort($result[$key]);
191
                $result[$out] = isset($result[$key][$index]) ? $result[$key][$index] : null;
192
                unset($result[$key]);
193
            }
194
        }
195
        return $results['result'];
196
    }
197
198
    /**
199
     * Get the Average metrics for a URL
200
     *
201
     * This will group data by date and returns only the
202
     * avg + date, making the data ideal for time series graphs
203
     *
204
     * @param string $url
205
     * @param array $search Search options containing date_start and or date_end
206
     * @return array Array of metrics grouped by date
207
     */
208
    public function getAvgsForUrl($url, $search = array())
209
    {
210
        $match = array('meta.simple_url' => $url);
211
        if (isset($search['date_start'])) {
212
            $match['meta.request_date']['$gte'] = (string)$search['date_start'];
213
        }
214
        if (isset($search['date_end'])) {
215
            $match['meta.request_date']['$lte'] = (string)$search['date_end'];
216
        }
217
        $results = $this->_collection->aggregate(array(
218
            array('$match' => $match),
219
            array(
220
                '$project' => array(
221
                    'date' => '$meta.request_date',
222
                    'profile.main()' => 1,
223
                )
224
            ),
225
            array(
226
                '$group' => array(
227
                    '_id' => '$date',
228
                    'avg_wt' => array('$avg' => '$profile.main().wt'),
229
                    'avg_cpu' => array('$avg' => '$profile.main().cpu'),
230
                    'avg_mu' => array('$avg' => '$profile.main().mu'),
231
                    'avg_pmu' => array('$avg' => '$profile.main().pmu'),
232
                )
233
            ),
234
            array('$sort' => array('_id' => 1))
235
        ),
236
            array('cursor' => array('batchSize' => 0))
237
        );
238
        if (empty($results['result'])) {
239
            return array();
240
        }
241
        foreach ($results['result'] as $i => $result) {
242
            $results['result'][$i]['date'] = $result['_id'];
243
            unset($results['result'][$i]['_id']);
244
        }
245
        return $results['result'];
246
    }
247
248
    /**
249
     * Get a paginated set of results.
250
     *
251
     * @param array $options The find options to use.
252
     * @return array An array of result data.
253
     */
254
    public function getAll($options = array())
255
    {
256
        return $this->paginate($options);
257
    }
258
259
    /**
260
     * Insert a profile run.
261
     *
262
     * Does unchecked inserts.
263
     *
264
     * @param array $profile The profile data to save.
265
     */
266
    public function insert($profile)
267
    {
268
        return $this->_collection->insert($profile, array('w' => 0));
269
    }
270
271
    /**
272
     * Delete a profile run.
273
     *
274
     * @param string $id The profile id to delete.
275
     * @return array|bool
276
     */
277
    public function delete($id)
278
    {
279
        return $this->_collection->remove(array('_id' => new MongoId($id)), array());
280
    }
281
282
    /**
283
     * Used to truncate a collection.
284
     *
285
     * Primarly used in test cases to reset the test db.
286
     *
287
     * @return boolean
288
     */
289
    public function truncate()
290
    {
291
        return $this->_collection->drop();
292
    }
293
294
    /**
295
     * Converts arrays + MongoCursors into Xhgui_Profile instances.
296
     *
297
     * @param array|MongoCursor $data The data to transform.
298
     * @return Xhgui_Profile|array The transformed/wrapped results.
299
     */
300
    protected function _wrap($data)
301
    {
302
        if ($data === null) {
303
            throw new Exception('No profile data found.');
304
        }
305
306
        if (is_array($data)) {
307
            return new Xhgui_Profile($data);
308
        }
309
        $results = array();
310
        foreach ($data as $row) {
311
            $results[] = new Xhgui_Profile($row);
312
        }
313
        return $results;
314
    }
315
}
316