Completed
Push — master ( 8850ba...7bafab )
by Tobias
22:02
created

CacheDataCollector::getCasters()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
rs 8.8571
cc 6
eloc 8
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of php-cache\cache-bundle package.
5
 *
6
 * (c) 2015 Aaron Scherer <[email protected]>, Tobias Nyholm <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Cache\CacheBundle\DataCollector;
13
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
17
use Symfony\Component\VarDumper\Caster\CutStub;
18
use Symfony\Component\VarDumper\Cloner\Stub;
19
use Symfony\Component\VarDumper\Cloner\VarCloner;
20
21
/**
22
 * @author Aaron Scherer <[email protected]>
23
 * @author Tobias Nyholm <[email protected]>
24
 *
25
 * @internal
26
 */
27
class CacheDataCollector extends DataCollector
28
{
29
    /**
30
     * @type CacheProxyInterface[]
31
     */
32
    private $instances = [];
33
34
    /**
35
     * @type VarCloner
36
     */
37
    private $cloner = null;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
38
39
    /**
40
     * @param string              $name
41
     * @param CacheProxyInterface $instance
42
     */
43
    public function addInstance($name, CacheProxyInterface $instance)
44
    {
45
        $this->instances[$name] = $instance;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function collect(Request $request, Response $response, \Exception $exception = null)
52
    {
53
        $empty      = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []];
54
        $this->data = ['instances' => $empty, 'total' => $empty];
55
        foreach ($this->instances as $name => $instance) {
56
            $calls = $instance->__getCalls();
57
            foreach ($calls as $call) {
58
                if (isset($call->result)) {
59
                    $call->result = $this->cloneData($call->result);
60
                }
61
                if (isset($call->argument)) {
62
                    $call->argument = $this->cloneData($call->argument);
63
                }
64
            }
65
            $this->data['instances']['calls'][$name] = $calls;
66
        }
67
68
        $this->data['instances']['statistics'] = $this->calculateStatistics();
69
        $this->data['total']['statistics']     = $this->calculateTotalStatistics();
70
    }
71
72
    /**
73
     * To be compatible with many versions of Symfony.
74
     *
75
     * @param $var
76
     */
77
    private function cloneData($var)
78
    {
79
        if (method_exists($this, 'cloneVar')) {
80
            // Symfony 3.2 or higher
81
            return $this->cloneVar($var);
82
        }
83
84
        if (null === $this->cloner) {
85
            $this->cloner = new VarCloner();
86
            $this->cloner->setMaxItems(-1);
87
            $this->cloner->addCasters($this->getCasters());
88
        }
89
90
        return $this->cloner->cloneVar($var);
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function getName()
97
    {
98
        return 'php-cache';
99
    }
100
101
    /**
102
     * Method returns amount of logged Cache reads: "get" calls.
103
     *
104
     * @return array
105
     */
106
    public function getStatistics()
107
    {
108
        return $this->data['instances']['statistics'];
109
    }
110
111
    /**
112
     * Method returns the statistic totals.
113
     *
114
     * @return array
115
     */
116
    public function getTotals()
117
    {
118
        return $this->data['total']['statistics'];
119
    }
120
121
    /**
122
     * Method returns all logged Cache call objects.
123
     *
124
     * @return mixed
125
     */
126
    public function getCalls()
127
    {
128
        return $this->data['instances']['calls'];
129
    }
130
131
    /**
132
     * @return array
133
     */
134
    private function calculateStatistics()
135
    {
136
        $statistics = [];
137
        foreach ($this->data['instances']['calls'] as $name => $calls) {
138
            $statistics[$name] = [
139
                'calls'   => 0,
140
                'time'    => 0,
141
                'reads'   => 0,
142
                'writes'  => 0,
143
                'deletes' => 0,
144
                'hits'    => 0,
145
                'misses'  => 0,
146
            ];
147
            /** @type TraceableAdapterEvent $call */
148
            foreach ($calls as $call) {
149
                $statistics[$name]['calls'] += 1;
150
                $statistics[$name]['time'] += $call->end - $call->start;
151
                if ('getItem' === $call->name) {
152
                    $statistics[$name]['reads'] += 1;
153 View Code Duplication
                    if ($call->hits) {
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...
154
                        $statistics[$name]['hits'] += 1;
155
                    } else {
156
                        $statistics[$name]['misses'] += 1;
157
                    }
158
                } elseif ('getItems' === $call->name) {
159
                    $count = $call->hits + $call->misses;
160
                    $statistics[$name]['reads'] += $count;
161
                    $statistics[$name]['hits'] += $call->hits;
162
                    $statistics[$name]['misses'] += $count - $call->misses;
163
                } elseif ('hasItem' === $call->name) {
164
                    $statistics[$name]['reads'] += 1;
165 View Code Duplication
                    if (false === $call->result) {
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...
166
                        $statistics[$name]['misses'] += 1;
167
                    } else {
168
                        $statistics[$name]['hits'] += 1;
169
                    }
170
                } elseif ('save' === $call->name) {
171
                    $statistics[$name]['writes'] += 1;
172
                } elseif ('deleteItem' === $call->name) {
173
                    $statistics[$name]['deletes'] += 1;
174
                }
175
            }
176
            if ($statistics[$name]['reads']) {
177
                $statistics[$name]['hit_read_ratio'] = round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2);
178
            } else {
179
                $statistics[$name]['hit_read_ratio'] = null;
180
            }
181
        }
182
183
        return $statistics;
184
    }
185
186
    /**
187
     * @return array
188
     */
189
    private function calculateTotalStatistics()
190
    {
191
        $statistics = $this->getStatistics();
192
        $totals     = [
193
            'calls'   => 0,
194
            'time'    => 0,
195
            'reads'   => 0,
196
            'writes'  => 0,
197
            'deletes' => 0,
198
            'hits'    => 0,
199
            'misses'  => 0,
200
        ];
201
        foreach ($statistics as $name => $values) {
202
            foreach ($totals as $key => $value) {
203
                $totals[$key] += $statistics[$name][$key];
204
            }
205
        }
206
        if ($totals['reads']) {
207
            $totals['hit_read_ratio'] = round(100 * $totals['hits'] / $totals['reads'], 2);
208
        } else {
209
            $totals['hit_read_ratio'] = null;
210
        }
211
212
        return $totals;
213
    }
214
215
    /**
216
     * @return callable[] The casters to add to the cloner
217
     */
218
    private function getCasters()
219
    {
220
        return [
221
            '*' => function ($v, array $a, Stub $s, $isNested) {
0 ignored issues
show
Unused Code introduced by
The parameter $s is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $isNested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
222
                if (!$v instanceof Stub) {
223
                    foreach ($a as $k => $v) {
224
                        if (is_object($v) && !$v instanceof \DateTimeInterface && !$v instanceof Stub) {
225
                            $a[$k] = new CutStub($v);
226
                        }
227
                    }
228
                }
229
230
                return $a;
231
            },
232
        ];
233
    }
234
}
235