Helper   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 251
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 4

Test Coverage

Coverage 93.75%

Importance

Changes 0
Metric Value
dl 0
loc 251
ccs 105
cts 112
cp 0.9375
rs 8.72
c 0
b 0
f 0
wmc 46
lcom 0
cbo 4

13 Methods

Rating   Name   Duplication   Size   Complexity  
A serialize() 0 10 2
A unserialize() 0 18 4
A compress() 0 6 2
A uncompress() 0 6 2
B compact() 0 20 7
A generateTrace() 0 8 2
B cleanupTrace() 0 26 9
B cleanupTraceArguments() 0 23 6
A hash() 0 11 2
A formatAsQuery() 0 10 2
A formatAsJSON() 0 5 2
A formatAsXML() 0 9 2
A formatAsHTML() 0 12 4

How to fix   Complexity   

Complex Class

Complex classes like Helper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Helper, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Provides a number of helper functions for the audit component
4
 */
5
6
namespace bedezign\yii2\audit\components;
7
8
use bedezign\yii2\audit\Audit;
9
use yii\helpers\ArrayHelper;
10
use yii\helpers\VarDumper;
11
12
/**
13
 * Helper
14
 * @package bedezign\yii2\audit\components
15
 */
16
class Helper extends \yii\base\BaseObject
17
{
18
    /**
19
     * Convert the given value into a gzip compressed blob so it can be stored in the database
20
     * @param mixed $data
21
     * @param bool $compact true to call the {@link compact()} function first
22
     * @return string               binary blob of data
23
     */
24 90
    public static function serialize($data, $compact = true)
25
    {
26
        if ($compact)
27 90
            $data = self::compact($data);
28
29 90
        $data = serialize($data);
30 90
        $data = self::compress($data);
31
32 90
        return $data;
33
    }
34
35
    /**
36
     * Re-inflates and unserializes a blob of compressed data
37
     * @param string $data
38
     * @return mixed            false if an error occurred
39
     */
40 96
    public static function unserialize($data)
41
    {
42 96
        if (is_resource($data))
43
            // For some databases (like Postgres) binary columns return as a resource, fetch the content first
44 96
            $data = stream_get_contents($data, -1, 0);
45
46 96
        $originalData = $data;
47 96
        $data = self::uncompress($data);
48
49 96
        if ($data === false)
50 96
            $data = $originalData;
51
52 96
        $data = @unserialize($data);
53 96
        if ($data === false)
54 96
            $data = $originalData;
55
56 96
        return $data;
57
    }
58
59
    /**
60
     * Compress
61
     * @param mixed $data
62
     * @return string binary blob of data
63
     */
64 90
    public static function compress($data)
65
    {
66 90
        if (Audit::getInstance()->compressData)
67 90
            $data = gzcompress($data);
68 90
        return $data;
69
    }
70
71
    /**
72
     * Compress
73
     * @param mixed $data
74
     * @return string binary blob of data
75
     */
76 96
    public static function uncompress($data)
77
    {
78 96
        $originalData = $data;
79 96
        $data = @gzuncompress($data);
80 96
        return $data ?: $originalData;
81
    }
82
83
    /**
84
     * Enumerate an array and get rid of the values that would exceed the $threshold size when serialized
85
     * @param array $data Non-array data will be converted to an array
86
     * @param bool $simplify If true, replace single valued arrays by just its value.
87
     * @param int $threshold serialized size to use as maximum
88
     * @return array
89
     */
90 27
    public static function compact($data, $simplify = false, $threshold = 512)
91 27
    {
92 9
        $data = ArrayHelper::toArray($data);
93
        if ($simplify)
94 3
            array_walk($data, function (&$value) {
95 3
                if (is_array($value) && count($value) == 1) $value = reset($value);
96 3
            });
97
98 9
        $tooBig = [];
99 9
        foreach ($data as $index => $value)
100 9
            if (strlen(serialize($value)) > $threshold)
101 9
                $tooBig[] = $index;
102
103 9
        if (count($tooBig)) {
104 3
            $data = array_diff_key($data, array_flip($tooBig));
105 3
            $data['__removed'] = $tooBig;
106 3
        }
107
108 9
        return $data;
109
    }
110
111
    /**
112
     * Generate a stacktrace and clean it (usually for regular errors)
113
     * @param int $skip Amount of entries to skip (usually 1 or 2). 2 is assuming this helper function and your logging function
114
     * @return array
115
     */
116
    public static function generateTrace($skip = 2)
117
    {
118
        $trace = debug_backtrace();
119
        array_pop($trace); // remove the last trace since it would be the entry script, not very useful
120
        if ($skip > 0)
121
            $trace = array_slice($trace, $skip);
122
        return self::cleanupTrace($trace);
123
    }
124
125
    /**
126
     * Normalize a stack trace so that all entries have the same keys and cleanup the arguments (removes anything that
127
     * cannot be serialized).
128
     * @param array $trace
129
     * @return array
130
     */
131 33
    public static function cleanupTrace($trace)
132
    {
133 33
        if (!is_array($trace))
134 33
            return [];
135
136 33
        foreach ($trace as $n => $call) {
137 27
            $call['file'] = isset($call['file']) ? $call['file'] : 'unknown';
138 27
            $call['line'] = isset($call['line']) ? $call['line'] : 0;
139 27
            $call['function'] = isset($call['function']) ? $call['function'] : 'unknown';
140 27
            $call['file'] = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $call['file']);
141
142
            // XDebug
143 27
            if (isset($call['params'])) unset($call['params']);
144
145
            // Trace entry contains the class instance, also compact and include this
146 27
            if (isset($call['object']))
147 27
                $call['object'] = current(self::cleanupTraceArguments([$call['object']]));
148
149 27
            if (isset($call['args']))
150 27
                $call['args'] = self::cleanupTraceArguments($call['args']);
151
152 27
            $trace[$n] = $call;
153 33
        }
154
155 33
        return $trace;
156
    }
157
158
    /**
159
     * Cleans up the given data so it can be serialized
160
     * @param     $args
161
     * @param int $recurseDepth Amount of recursion cycles before we start replacing data with "Array" etc
162
     * @return mixed
163
     */
164 27
    public static function cleanupTraceArguments($args, $recurseDepth = 3)
165
    {
166 27
        foreach ($args as $name => $value) {
167 27
            if (is_object($value)) {
168 27
                $class = get_class($value);
169
                // By default we just mention the object type
170 27
                $args[$name] = 'Object(' . $class . ')';
171
172 27
                if ($recurseDepth > 0) {
173
                    // Make sure to limit the toArray to non recursive, it's much to easy to get stuck in an infinite loop
174 27
                    $object = self::cleanupTraceArguments(ArrayHelper::toArray($value, [], false), $recurseDepth - 1);
175 27
                    $object['__class'] = $class;
176 27
                    $args[$name] = $object;
177 27
                }
178 27
            } else if (is_array($value)) {
179 27
                if ($recurseDepth > 0)
180 27
                    $args[$name] = self::cleanupTraceArguments($value, $recurseDepth - 1);
181
                else
182 21
                    $args[$name] = 'Array';
183 27
            }
184 27
        }
185 27
        return $args;
186
    }
187
188
    /**
189
     * Hash a long string to a short string.
190
     * @link http://au1.php.net/crc32#111931
191
     *
192
     * @param $data
193
     * @return string
194
     */
195 33
    public static function hash($data)
196
    {
197 33
        static $map = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
198 33
        $hash = bcadd(sprintf('%u', crc32($data)), 0x100000000);
199 33
        $str = '';
200
        do {
201 33
            $str = $map[bcmod($hash, 62)] . $str;
202 33
            $hash = bcdiv($hash, 62);
203 33
        } while ($hash >= 1);
204 33
        return $str;
205
    }
206
207
    /**
208
     * If the data resembles a query string it will be returned as a formatted variable for output
209
     * @param $data
210
     * @return null|string
211
     */
212 9
    public static function formatAsQuery($data)
213
    {
214 9
        $data = rawurldecode($data);
215 9
        if (!preg_match('/^([\w\d\-\[\]]+(=[^&]*)?(&[\w\d\-\[\]]+(=[^&]*)?)*)?$/', $data))
216 9
            return null;
217
218 3
        $result = [];
219 3
        parse_str($data, $result);
220 3
        return VarDumper::dumpAsString($result, 15);
221
    }
222
223
    /**
224
     * If the data contains JSON it will be returned as a pretty printable string
225
     * @param $data
226
     * @return null|string
227
     */
228 9
    public static function formatAsJSON($data)
229
    {
230 9
        $decoded = @json_decode($data);
231 9
        return $decoded ? json_encode($decoded, JSON_PRETTY_PRINT) : null;
232
    }
233
234
    /**
235
     * If the data contains XML it will be returned as a pretty printable string
236
     * @param $data
237
     * @return null|string
238
     */
239 6
    public static function formatAsXML($data)
240
    {
241 6
        $doc = new \DOMDocument('1.0');
242 6
        $doc->preserveWhiteSpace = false;
243 6
        $doc->formatOutput = true;
244 6
        if (@$doc->loadXML($data))
245 6
            return htmlentities($doc->saveXML(), ENT_COMPAT, 'UTF-8');
246 3
        return null;
247
    }
248
249
    /**
250
     * If the data contains HTML it will be returned as a pretty printable string
251
     * @param $data
252
     * @return null|string
253
     */
254 6
    public static function formatAsHTML($data)
255
    {
256 6
        if ($data == strip_tags($data) || strtolower(substr(ltrim($data), 0, 5)) == '<?xml')
257 6
            return null;
258
259 3
        $doc = new \DOMDocument('1.0');
260 3
        $doc->preserveWhiteSpace = false;
261 3
        $doc->formatOutput = true;
262 3
        if (@$doc->loadHTML($data))
263 3
            return htmlentities($doc->saveHTML(), ENT_COMPAT, 'UTF-8');
264
        return null;
265
    }
266
}