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 | } |