| Total Complexity | 107 |
| Total Lines | 482 |
| Duplicated Lines | 0 % |
| Changes | 3 | ||
| Bugs | 1 | Features | 1 |
Complex classes like SyncObject 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.
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 SyncObject, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 13 | abstract class SyncObject extends Streamer implements Stringable { |
||
| 14 | public const STREAMER_CHECKS = 6; |
||
| 15 | public const STREAMER_CHECK_REQUIRED = 7; |
||
| 16 | public const STREAMER_CHECK_ZEROORONE = 8; |
||
| 17 | public const STREAMER_CHECK_NOTALLOWED = 9; |
||
| 18 | public const STREAMER_CHECK_ONEVALUEOF = 10; |
||
| 19 | public const STREAMER_CHECK_SETZERO = "setToValue0"; |
||
| 20 | public const STREAMER_CHECK_SETONE = "setToValue1"; |
||
| 21 | public const STREAMER_CHECK_SETTWO = "setToValue2"; |
||
| 22 | public const STREAMER_CHECK_SETEMPTY = "setToValueEmpty"; |
||
| 23 | public const STREAMER_CHECK_CMPLOWER = 13; |
||
| 24 | public const STREAMER_CHECK_CMPHIGHER = 14; |
||
| 25 | public const STREAMER_CHECK_LENGTHMAX = 15; |
||
| 26 | public const STREAMER_CHECK_EMAIL = 16; |
||
| 27 | |||
| 28 | protected $unsetVars; |
||
| 29 | protected $supportsPrivateStripping; |
||
| 30 | protected $checkedParameters; |
||
| 31 | |||
| 32 | public function __construct($mapping) { |
||
| 33 | $this->unsetVars = []; |
||
| 34 | $this->supportsPrivateStripping = false; |
||
| 35 | $this->checkedParameters = false; |
||
| 36 | parent::__construct($mapping); |
||
| 37 | } |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Sets all supported but not transmitted variables |
||
| 41 | * of this SyncObject to an "empty" value, so they are deleted when being saved. |
||
| 42 | * |
||
| 43 | * @param array $supportedFields array with all supported fields, if available |
||
| 44 | * |
||
| 45 | * @return bool |
||
| 46 | */ |
||
| 47 | public function emptySupported($supportedFields) { |
||
| 48 | // Some devices do not send supported tag. In such a case remove all not set properties. |
||
| 49 | if ($supportedFields === false || !is_array($supportedFields) || (empty($supportedFields))) { |
||
|
|
|||
| 50 | if (defined('UNSET_UNDEFINED_PROPERTIES') && |
||
| 51 | UNSET_UNDEFINED_PROPERTIES && |
||
| 52 | ( |
||
| 53 | $this instanceof SyncContact || |
||
| 54 | $this instanceof SyncAppointment || |
||
| 55 | $this instanceof SyncTask |
||
| 56 | )) { |
||
| 57 | SLog::Write(LOGLEVEL_INFO, sprintf("%s->emptySupported(): no supported list available, emptying all not set parameters", static::class)); |
||
| 58 | $supportedFields = array_keys($this->mapping); |
||
| 59 | } |
||
| 60 | else { |
||
| 61 | return false; |
||
| 62 | } |
||
| 63 | } |
||
| 64 | |||
| 65 | foreach ($supportedFields as $field) { |
||
| 66 | if (!isset($this->mapping[$field])) { |
||
| 67 | SLog::Write(LOGLEVEL_WARN, sprintf("Field '%s' is supposed to be emptied but is not defined for '%s'", $field, static::class)); |
||
| 68 | |||
| 69 | continue; |
||
| 70 | } |
||
| 71 | $var = $this->mapping[$field][self::STREAMER_VAR]; |
||
| 72 | // add var to $this->unsetVars if $var is not set |
||
| 73 | if (!isset($this->{$var})) { |
||
| 74 | $this->unsetVars[] = $var; |
||
| 75 | } |
||
| 76 | } |
||
| 77 | SLog::Write(LOGLEVEL_DEBUG, sprintf("Supported variables to be unset: %s", implode(',', $this->unsetVars))); |
||
| 78 | |||
| 79 | return true; |
||
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * Compares this a SyncObject to another. |
||
| 84 | * In case that all available mapped fields are exactly EQUAL, it returns true. |
||
| 85 | * |
||
| 86 | * @see SyncObject |
||
| 87 | * |
||
| 88 | * @param SyncObject $odo other SyncObject |
||
| 89 | * @param bool $log flag to turn on logging |
||
| 90 | * @param bool $strictTypeCompare to enforce type matching |
||
| 91 | * |
||
| 92 | * @return bool |
||
| 93 | */ |
||
| 94 | public function equals($odo, $log = false, $strictTypeCompare = false) { |
||
| 95 | if ($odo === false) { |
||
| 96 | return false; |
||
| 97 | } |
||
| 98 | |||
| 99 | // check objecttype |
||
| 100 | if (!$odo instanceof SyncObject) { |
||
| 101 | SLog::Write(LOGLEVEL_DEBUG, "SyncObject->equals() the target object is not a SyncObject"); |
||
| 102 | |||
| 103 | return false; |
||
| 104 | } |
||
| 105 | |||
| 106 | // check for mapped fields |
||
| 107 | foreach ($this->mapping as $v) { |
||
| 108 | $val = $v[self::STREAMER_VAR]; |
||
| 109 | // array of values? |
||
| 110 | if (isset($v[self::STREAMER_ARRAY])) { |
||
| 111 | // if neither array is created then don't fail the comparison |
||
| 112 | if (!isset($this->{$val}) && !isset($odo->{$val})) { |
||
| 113 | SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() array '%s' is NOT SET in either object", $val)); |
||
| 114 | |||
| 115 | continue; |
||
| 116 | } |
||
| 117 | if (is_array($this->{$val}) && is_array($odo->{$val})) { |
||
| 118 | // if both arrays exist then seek for differences in the arrays |
||
| 119 | if (count(array_diff($this->{$val}, $odo->{$val})) + count(array_diff($odo->{$val}, $this->{$val})) > 0) { |
||
| 120 | SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() items in array '%s' differ", $val)); |
||
| 121 | |||
| 122 | return false; |
||
| 123 | } |
||
| 124 | } |
||
| 125 | else { |
||
| 126 | SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() array '%s' is set in one but not the other object", $val)); |
||
| 127 | |||
| 128 | return false; |
||
| 129 | } |
||
| 130 | } |
||
| 131 | else { |
||
| 132 | if (isset($this->{$val}, $odo->{$val})) { |
||
| 133 | if ($strictTypeCompare) { |
||
| 134 | if ($this->{$val} !== $odo->{$val}) { |
||
| 135 | SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() false on field '%s': '%s' != '%s' using strictTypeCompare", $val, Utils::PrintAsString($this->{$val}), Utils::PrintAsString($odo->{$val}))); |
||
| 136 | |||
| 137 | return false; |
||
| 138 | } |
||
| 139 | } |
||
| 140 | else { |
||
| 141 | if ($this->{$val} != $odo->{$val}) { |
||
| 142 | SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() false on field '%s': '%s' != '%s'", $val, Utils::PrintAsString($this->{$val}), Utils::PrintAsString($odo->{$val}))); |
||
| 143 | |||
| 144 | return false; |
||
| 145 | } |
||
| 146 | } |
||
| 147 | } |
||
| 148 | elseif (!isset($this->{$val}) && !isset($odo->{$val})) { |
||
| 149 | continue; |
||
| 150 | } |
||
| 151 | else { |
||
| 152 | SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() false because field '%s' is only defined at one obj: '%s' != '%s'", $val, Utils::PrintAsString(isset($this->{$val})), Utils::PrintAsString(isset($odo->{$val})))); |
||
| 153 | |||
| 154 | return false; |
||
| 155 | } |
||
| 156 | } |
||
| 157 | } |
||
| 158 | |||
| 159 | return true; |
||
| 160 | } |
||
| 161 | |||
| 162 | /** |
||
| 163 | * String representation of the object. |
||
| 164 | */ |
||
| 165 | public function __toString(): string { |
||
| 166 | $str = static::class . " (\n"; |
||
| 167 | |||
| 168 | $streamerVars = []; |
||
| 169 | foreach ($this->mapping as $k => $v) { |
||
| 170 | $streamerVars[$v[self::STREAMER_VAR]] = $v[self::STREAMER_TYPE] ?? false; |
||
| 171 | } |
||
| 172 | |||
| 173 | foreach (get_object_vars($this) as $k => $v) { |
||
| 174 | if ($k == "mapping") { |
||
| 175 | continue; |
||
| 176 | } |
||
| 177 | |||
| 178 | if (array_key_exists($k, $streamerVars)) { |
||
| 179 | $strV = "(S) "; |
||
| 180 | } |
||
| 181 | else { |
||
| 182 | $strV = ""; |
||
| 183 | } |
||
| 184 | |||
| 185 | // self::STREAMER_ARRAY ? |
||
| 186 | if (is_array($v)) { |
||
| 187 | $str .= "\t" . $strV . $k . "(Array) size: " . count($v) . "\n"; |
||
| 188 | foreach ($v as $value) { |
||
| 189 | $str .= "\t\t" . Utils::PrintAsString($value) . "\n"; |
||
| 190 | } |
||
| 191 | } |
||
| 192 | elseif ($v instanceof SyncObject) { |
||
| 193 | $str .= "\t" . $strV . $k . " => " . str_replace("\n", "\n\t\t\t", $v->__toString()) . "\n"; |
||
| 194 | } |
||
| 195 | else { |
||
| 196 | $str .= "\t" . $strV . $k . " => " . (isset($this->{$k}) ? Utils::PrintAsString($this->{$k}) : "null") . "\n"; |
||
| 197 | } |
||
| 198 | } |
||
| 199 | $str .= ")"; |
||
| 200 | |||
| 201 | return $str; |
||
| 202 | } |
||
| 203 | |||
| 204 | /** |
||
| 205 | * Returns the properties which have to be unset on the server. |
||
| 206 | * |
||
| 207 | * @return array |
||
| 208 | */ |
||
| 209 | public function getUnsetVars() { |
||
| 210 | return $this->unsetVars; |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * Removes not necessary data from the object. |
||
| 215 | * |
||
| 216 | * @param mixed $flags |
||
| 217 | * |
||
| 218 | * @return bool |
||
| 219 | */ |
||
| 220 | #[Override] |
||
| 221 | public function StripData($flags = 0) { |
||
| 222 | if ($flags === 0 && isset($this->unsetVars)) { |
||
| 223 | unset($this->unsetVars); |
||
| 224 | } |
||
| 225 | |||
| 226 | return parent::StripData($flags); |
||
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Indicates if a SyncObject supports the private flag and stripping of private data. |
||
| 231 | * If an object does not support it, it will not be sent to the client but permanently be excluded from the sync. |
||
| 232 | * |
||
| 233 | * @return bool - default false defined in constructor - overwritten by implementation |
||
| 234 | */ |
||
| 235 | public function SupportsPrivateStripping() { |
||
| 237 | } |
||
| 238 | |||
| 239 | /** |
||
| 240 | * Indicates the amount of parameters that were set before Checks were executed and potentially set other parameters. |
||
| 241 | * |
||
| 242 | * @return bool/int - returns false if Check() was not executed |
||
| 243 | */ |
||
| 244 | public function getCheckedParameters() { |
||
| 246 | } |
||
| 247 | |||
| 248 | /** |
||
| 249 | * Method checks if the object has the minimum of required parameters |
||
| 250 | * and fulfills semantic dependencies. |
||
| 251 | * |
||
| 252 | * General checks: |
||
| 253 | * STREAMER_CHECK_REQUIRED may have as value false (do not fix, ignore object!) or set-to-values: STREAMER_CHECK_SETZERO/ONE/TWO, STREAMER_CHECK_SETEMPTY |
||
| 254 | * STREAMER_CHECK_ZEROORONE may be 0 or 1, if none of these, set-to-values: STREAMER_CHECK_SETZERO or STREAMER_CHECK_SETONE |
||
| 255 | * STREAMER_CHECK_NOTALLOWED fails if is set |
||
| 256 | * STREAMER_CHECK_ONEVALUEOF expects an array with accepted values, fails if value is not in array |
||
| 257 | * |
||
| 258 | * Comparison: |
||
| 259 | * STREAMER_CHECK_CMPLOWER compares if the current parameter is lower as a literal or another parameter of the same object |
||
| 260 | * STREAMER_CHECK_CMPHIGHER compares if the current parameter is higher as a literal or another parameter of the same object |
||
| 261 | * |
||
| 262 | * @param bool $logAsDebug (opt) default is false, so messages are logged in WARN log level |
||
| 263 | * |
||
| 264 | * @return bool |
||
| 265 | */ |
||
| 266 | public function Check($logAsDebug = false) { |
||
| 474 | } |
||
| 475 | |||
| 476 | /** |
||
| 477 | * Returns human friendly property name from its value if a mapping is available. |
||
| 478 | * |
||
| 479 | * @param array $v |
||
| 480 | * @param mixed $val |
||
| 481 | * |
||
| 482 | * @return mixed |
||
| 483 | */ |
||
| 484 | public function GetNameFromPropertyValue($v, $val) { |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * Called after the SyncObject was unserialized. |
||
| 490 | * |
||
| 491 | * @return bool |
||
| 492 | */ |
||
| 493 | public function postUnserialize() { |
||
| 495 | } |
||
| 496 | } |
||
| 497 |