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 |
||
16 | class Helper extends \yii\base\Object |
||
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 | 60 | public static function serialize($data, $compact = true) |
|
25 | { |
||
26 | if ($compact) |
||
27 | 60 | $data = self::compact($data); |
|
28 | |||
29 | 60 | $data = serialize($data); |
|
30 | 60 | $data = self::compress($data); |
|
31 | |||
32 | 60 | 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 | 64 | public static function unserialize($data) |
|
41 | { |
||
42 | 64 | if (is_resource($data)) |
|
43 | // For some databases (like Postgres) binary columns return as a resource, fetch the content first |
||
44 | 64 | $data = stream_get_contents($data, -1, 0); |
|
45 | |||
46 | 64 | $originalData = $data; |
|
47 | 64 | $data = self::uncompress($data); |
|
48 | |||
49 | 64 | if ($data === false) |
|
50 | 64 | $data = $originalData; |
|
51 | |||
52 | 64 | $data = @unserialize($data); |
|
53 | 64 | if ($data === false) |
|
54 | 64 | $data = $originalData; |
|
55 | |||
56 | 64 | return $data; |
|
57 | } |
||
58 | |||
59 | /** |
||
60 | * Compress |
||
61 | * @param mixed $data |
||
62 | * @return string binary blob of data |
||
63 | */ |
||
64 | 60 | public static function compress($data) |
|
65 | { |
||
66 | 60 | if (Audit::getInstance()->compressData) |
|
67 | 60 | $data = gzcompress($data); |
|
68 | 60 | return $data; |
|
69 | } |
||
70 | |||
71 | /** |
||
72 | * Compress |
||
73 | * @param mixed $data |
||
74 | * @return string binary blob of data |
||
75 | */ |
||
76 | 64 | public static function uncompress($data) |
|
77 | { |
||
78 | 64 | $originalData = $data; |
|
79 | 64 | $data = @gzuncompress($data); |
|
80 | 64 | 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 | 18 | public static function compact($data, $simplify = false, $threshold = 512) |
|
91 | 18 | { |
|
92 | 6 | $data = ArrayHelper::toArray($data); |
|
93 | if ($simplify) |
||
94 | 2 | array_walk($data, function (&$value) { |
|
95 | 2 | if (is_array($value) && count($value) == 1) $value = reset($value); |
|
96 | 2 | }); |
|
97 | |||
98 | 6 | $tooBig = []; |
|
99 | 6 | foreach ($data as $index => $value) |
|
100 | 6 | if (strlen(serialize($value)) > $threshold) |
|
101 | 6 | $tooBig[] = $index; |
|
102 | |||
103 | 6 | if (count($tooBig)) { |
|
104 | 2 | $data = array_diff_key($data, array_flip($tooBig)); |
|
105 | 2 | $data['__removed'] = $tooBig; |
|
106 | 2 | } |
|
107 | |||
108 | 6 | 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 | 22 | public static function cleanupTrace($trace) |
|
132 | { |
||
133 | 22 | if (!is_array($trace)) |
|
134 | 22 | return []; |
|
135 | |||
136 | 22 | foreach ($trace as $n => $call) { |
|
137 | 18 | $call['file'] = isset($call['file']) ? $call['file'] : 'unknown'; |
|
138 | 18 | $call['line'] = isset($call['line']) ? $call['line'] : 0; |
|
139 | 18 | $call['function'] = isset($call['function']) ? $call['function'] : 'unknown'; |
|
140 | 18 | $call['file'] = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $call['file']); |
|
141 | |||
142 | // XDebug |
||
143 | 18 | if (isset($call['params'])) unset($call['params']); |
|
144 | |||
145 | // Trace entry contains the class instance, also compact and include this |
||
146 | 18 | if (isset($call['object'])) |
|
147 | 18 | $call['object'] = current(self::cleanupTraceArguments([$call['object']])); |
|
148 | |||
149 | 18 | if (isset($call['args'])) |
|
150 | 18 | $call['args'] = self::cleanupTraceArguments($call['args']); |
|
151 | |||
152 | 18 | $trace[$n] = $call; |
|
153 | 22 | } |
|
154 | |||
155 | 22 | 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 | 18 | public static function cleanupTraceArguments($args, $recurseDepth = 3) |
|
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 | 22 | public static function hash($data) |
|
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 | 6 | public static function formatAsQuery($data) |
|
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 | 6 | public static function formatAsJSON($data) |
|
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 | 4 | public static function formatAsXML($data) |
|
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 | 4 | public static function formatAsHTML($data) |
|
266 | } |