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

getMetafileNameFromProfileName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
class Xhgui_Storage_File implements \Xhgui_StorageInterface, \Xhgui_WatchedFunctionsStorageInterface
4
{
5
6
    /**
7
     * @var string
8
     */
9
    protected $path     = '../data/';
10
11
    /**
12
     * @var string
13
     */
14
    protected $prefix   = 'xhgui.data';
15
16
    /**
17
     * @var bool|mixed
18
     */
19
    protected $separateMeta = true;
20
21
    /**
22
     * @var mixed
23
     */
24
    protected $dataSerializer;
25
26
    /**
27
     * @var mixed
28
     */
29
    protected $metaSerializer;
30
31
    /**
32
     * @var string
33
     */
34
    protected $watchedFunctionsPathPrefix = '../watched_functions/';
35
36
    /**
37
     * @var int[]
38
     */
39
    protected $countCache;
40
41
    /**
42
     * Xhgui_Storage_File constructor.
43
     * @param $config
44
     */
45
    public function __construct($config)
46
    {
47
48
        // @todo config!
49
        $this->path         = '../data/';
50
51
        // @todo config!
52
        $this->prefix       = 'xhgui.data';
53
54
        $this->separateMeta     = $config['save.handler.separate_meta'];
55
        $this->dataSerializer   = $config['save.handler.serializer'];
56
        $this->metaSerializer   = $config['save.handler.meta_serializer'];
57
    }
58
59
    /**
60
     * @param Xhgui_Storage_Filter $filter
61
     * @param bool $projections
62
     * @return Xhgui_Storage_ResultSet
63
     */
64
    public function find(\Xhgui_Storage_Filter $filter, $projections = false)
65
    {
66
        $result       = glob($this->path. $this->prefix . '*');
67
        sort($result);
68
69
        $ret = [];
70
        foreach($result as $i => $file) {
71
            // skip meta files.
72
            if (strpos($file, '.meta') !== false) {
73
                continue;
74
            }
75
76
            // try to detect timestamp in filename.
77
            $requestTimeFromFilename = $this->getRequestTimeFromFilename($file);
78
            if (!empty($requestTimeFromFilename)) {
79
                if (null !== $filter->getStartDate() && $this->getDateTimeFromStringOrTimestamp($filter->getStartDate()) >= $requestTimeFromFilename) {
80
                    continue;
81
                }
82
83
                if (null !== $filter->getEndDate() && $this->getDateTimeFromStringOrTimestamp($filter->getEndDate()) <= $requestTimeFromFilename) {
84
                    continue;
85
                }
86
            }
87
88
            $metaFile   = $this->getMetafileNameFromProfileName($file);
89
90
            $meta       = $this->importFile($metaFile, true);
91
            if ($meta === false) {
92
                continue;
93
            }
94
95
            $profile    = $this->importFile($file, false);
96
            if ($profile === false) {
97
                continue;
98
            }
99
100
            if (!empty($profile['meta'])) {
101
                $meta = array_merge($meta, $profile['meta']);
102
            }
103
104
            if (!empty($profile['profile'])) {
105
                $profile = $profile['profile'];
106
            }
107
108
            if (!empty($profile['_id'])) {
109
                $id = $profile['_id'];
110
            } else {
111
                $id = basename($file);
112
            }
113
            if (!empty($profile)) {
114
                $ret[$id] = [
115
                    'profile'   => $profile,
116
                    '_id'       => $id,
117
                    'meta'      => $meta,
118
                ];
119
            } else {
120
                $ret[$id] = $profile;
121
            }
122
        }
123
124
        if (!empty($filter->getSort()) AND !empty($ret)) {
125
            $this->filter = $filter;
0 ignored issues
show
Bug introduced by
The property filter does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
126
            usort($ret, array($this, 'sortByColumn'));
127
            unset($this->filter);
128
        }
129
        $cacheId = md5(serialize($filter->toArray()));
130
131
        $this->countCache[$cacheId] = count($ret);
132
        $ret = array_slice($ret, $filter->getPerPage()*($filter->getPage()-1), $filter->getPerPage());
133
        $ret = array_column($ret, null, '_id');
134
135
        return new \Xhgui_Storage_ResultSet($ret, $this->countCache[$cacheId]);
136
    }
137
138
    /**
139
     * @param Xhgui_Storage_Filter $filter
140
     * @return int
141
     */
142
    public function count(\Xhgui_Storage_Filter $filter)
143
    {
144
        $cacheId = md5(serialize($filter->toArray()));
145
        if (empty($this->countCache[$cacheId])) {
146
            $this->find($filter);
147
        }
148
        return $this->countCache[$cacheId];
149
    }
150
151
    /**
152
     * @param $id
153
     * @return mixed
154
     */
155
    public function findOne($id)
156
    {
157
        $filter = new \Xhgui_Storage_Filter();
158
        $filter->setId($id);
159
        $resultSet = $this->find($id);
160
        return $resultSet->current();
161
    }
162
163
    /**
164
     * @param $id
165
     * @return bool
166
     */
167
    public function remove($id)
168
    {
169
        if (file_exists($this->path.$id)) {
170
            $metaFileName = $this->getMetafileNameFromProfileName($id);
171
            if (file_exists($this->path.$metaFileName)) {
172
                unlink($this->path.$metaFileName);
173
            }
174
            unlink($this->path.$id);
175
            return true;
176
        }
177
        return false;
178
    }
179
180
    /**
181
     *
182
     */
183
    public function drop()
184
    {
185
        array_map('unlink', glob($this->path.'*.xhprof'));
186
        array_map('unlink', glob($this->path.'*.meta'));
187
    }
188
189
    /**
190
     * @param $match
191
     * @param $col
192
     * @param int $percentile
193
     * @return array
194
     */
195
    public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1)
196
    {
197
        $ret = $this->find($filter);
198
199
        $result = [
200
            'ok'        => 1,
201
            'result'    => [],
202
        ];
203
204
        foreach($ret as $row) {
205
            $result['result'][$row['meta']['request_date']]['wall_times'][]    = $row['profile']['main()']['wt'];
206
            $result['result'][$row['meta']['request_date']]['cpu_times'][]     = $row['profile']['main()']['cpu'];
207
            $result['result'][$row['meta']['request_date']]['mu_times'][]      = $row['profile']['main()']['mu'];
208
            $result['result'][$row['meta']['request_date']]['pmu_times'][]     = $row['profile']['main()']['pmu'];
209
210
            if (empty($result['result'][$row['meta']['request_date']]['row_count'])) {
211
                $result['result'][$row['meta']['request_date']]['row_count'] = 0;
212
            }
213
            $result['result'][$row['meta']['request_date']]['row_count']++;
214
215
            $result['result'][$row['meta']['request_date']]['raw_index'] = $result['result'][$row['meta']['request_date']]['row_count']*($percentile/100);
216
217
            $result['result'][$row['meta']['request_date']]['_id']=$row['meta']['request_date'];
218
        }
219
220
        return $result;
221
    }
222
223
224
    /**
225
     * @param $a
226
     * @param $b
227
     * @return int
228
     */
229
    public function sortByColumn($a, $b)
230
    {
231
        $sort = $this->filter->getSort();
232
        switch($sort) {
233
            case 'ct':
234
            case 'wt':
235
            case 'cpu':
236
            case 'mu':
237 View Code Duplication
            case 'pmu':
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...
238
                $aValue = $a['profile']['main()'][$sort];
239
                $bValue = $b['profile']['main()'][$sort];
240
                break;
241
242 View Code Duplication
            case 'time':
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...
243
                $aValue = $a['meta']['request_ts']['sec'];
244
                $bValue = $b['meta']['request_ts']['sec'];
245
                break;
246
247
            case 'controller':
248
            case 'action':
249
            case 'application':
250
            case 'branch':
251 View Code Duplication
            case 'version':
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...
252
                $aValue = $a['meta'][$sort];
253
                $bValue = $b['meta'][$sort];
254
                break;
255
        }
256
257
        if ($aValue == $bValue){
0 ignored issues
show
Bug introduced by
The variable $aValue does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $bValue does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
258
            return 0;
259
        }
260
261
        if (is_numeric($aValue) || is_numeric($bValue)) {
262
            if ($this->filter->getDirection() === 'desc') {
263
                if ($aValue < $bValue) {
264
                    return 1;
265
                }
266
                return -1;
267
            }
268
269
            if ($aValue > $bValue) {
270
                return 1;
271
            }
272
            return -1;
273
        }
274
275
        if ($this->filter->getDirection() === 'desc') {
276
            return strnatcmp($aValue, $bValue);
277
        }
278
        return strnatcmp($bValue, $aValue);
279
    }
280
281
282
283
    /**
284
     * @param $file
285
     * @return mixed
286
     */
287
    protected function getMetafileNameFromProfileName($file)
288
    {
289
        $metaFile = $file.'.meta';
290
        return $metaFile;
291
    }
292
293
294
    /**
295
     * @param $timestamp
296
     * @return bool|DateTime
297
     */
298
    protected function getDateTimeFromStringOrTimestamp($timestamp)
299
    {
300
301
        try {
302
            $date = new \DateTime($timestamp);
303
            return $date;
304
        } catch(\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
305
        }
306
307
        try {
308
            $date = \DateTime::createFromFormat('U', $timestamp);
309
            return $date;
310
        } catch(\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
311
        }
312
313
        try {
314
            $date = \DateTime::createFromFormat('Y-m-d H:i:s', $timestamp);
315
            return $date;
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
319
        throw new \RuntimeException('Unable to parse date from string: '.$timestamp);
320
    }
321
322
    /**
323
     * @param $data
324
     */
325
    public function insert($data)
326
    {
327
328
        if (empty($data['_id'])) {
329
            $data['_id'] = md5($data['name']);
330
        }
331
332
        file_put_contents($this->path.''.$this->prefix.$data['_id'].'.json', json_encode($data));
333
    }
334
335
    /**
336
     * @param $id
337
     * @param $data
338
     */
339
    public function update($id, $data)
340
    {
341
        file_put_contents($this->path.''.$this->prefix.$id.'.json', json_encode($data));
342
    }
343
344
    /**
345
     * @param $path
346
     * @param bool $meta
347
     * @return mixed
348
     */
349
    protected function importFile($path, $meta = false)
350
    {
351
        if ($meta) {
352
            $serializer = $this->metaSerializer;
353
        } else {
354
            $serializer = $this->dataSerializer;
355
        }
356
357
        if (!file_exists($path) || !is_readable($path)) {
358
            return false;
359
        }
360
        
361
        switch ($serializer){
362
            default:
363
            case 'json':
364
                return json_decode(file_get_contents($path), true);
365
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
366
367
            case 'serialize':
368
                if (PHP_MAJOR_VERSION > 7) {
369
                    return unserialize(file_get_contents($path), false);
370
                }
371
                /** @noinspection UnserializeExploitsInspection */
372
                return unserialize(file_get_contents($path));
373
374
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
375
376
            case 'igbinary_serialize':
377
            case 'igbinary_unserialize':
378
            case 'igbinary':
379
                return igbinary_unserialize(file_get_contents($path));
380
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
381
382
            // this is a path to a file on disk
383
            case 'php':
384
            case 'var_export':
385
                return include $path;
386
        }
387
    }
388
389
    /**
390
     * @return array
391
     */
392
    public function getWatchedFunctions()
393
    {
394
        $ret = [];
395
        $files = glob($this->watchedFunctionsPathPrefix.'*.json');
396
        foreach ($files as $file) {
397
            $ret[] = json_decode(file_get_contents($file));
398
        }
399
        return $ret;
400
    }
401
402
    /**
403
     * @param $name
404
     * @return bool
405
     */
406 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...
407
    {
408
        $name = trim($name);
409
        if (empty($name)) {
410
            return false;
411
        }
412
        $id = md5($name);
413
        file_put_contents($this->watchedFunctionsPathPrefix.$id.'.json', json_encode(['id'=>$id, 'name'=>$name]));
414
    }
415
416
    /**
417
     * @param $id
418
     * @param $name
419
     * @return bool
420
     */
421 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...
422
    {
423
        $name = trim($name);
424
        if (empty($name)) {
425
            return false;
426
        }
427
428
        file_put_contents($this->watchedFunctionsPathPrefix.$id.'.json', json_encode(['id'=>$id, 'name'=>trim($name)]));
429
    }
430
431
    /**
432
     * @param $id
433
     */
434
    public function removeWatchedFunction($id)
435
    {
436
        if (file_exists($this->watchedFunctionsPathPrefix.$id.'.json')) {
437
            unlink($this->watchedFunctionsPathPrefix.$id.'.json');
438
        }
439
    }
440
441
    /**
442
     * @param $fileName
443
     * @return bool|\DateTime
444
     */
445
    public function getRequestTimeFromFilename($fileName)
446
    {
447
        $matches = [];
448
        // default pattern is: xhgui.data.<timestamp>.<microseconds>_a68888
449
        //  xhgui.data.15 55 31 04 66 .6606_a68888
450
        preg_match('/(?<t>[0-9]{10})(\.(?<m>[0-9]{1,6})){0,1}.+/i', $fileName, $matches);
451
        try {
452
            return \DateTime::createFromFormat('U u', $matches['t'].' '.$matches['m']);
453
        } catch (\Exception $e) {
454
            return null;
455
        }
456
    }
457
}
458