Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like JWT 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 JWT, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 29 | class JWT { |
||
| 30 | /** |
||
| 31 | * When checking nbf, iat or expiration times, |
||
| 32 | * we want to provide some extra leeway time to |
||
| 33 | * account for clock skew. |
||
| 34 | * |
||
| 35 | * @var int $leeway The leeway value. |
||
| 36 | */ |
||
| 37 | public static $leeway = 0; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Allow the current timestamp to be specified. |
||
| 41 | * Useful for fixing a value within unit testing. |
||
| 42 | * |
||
| 43 | * Will default to PHP time() value if null. |
||
| 44 | * |
||
| 45 | * @var string $timestamp The timestamp. |
||
| 46 | */ |
||
| 47 | public static $timestamp = null; |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Supported algorithms. |
||
| 51 | * |
||
| 52 | * @var array $supported_algs Supported algorithms. |
||
| 53 | */ |
||
| 54 | public static $supported_algs = array( |
||
| 55 | 'HS256' => array( 'hash_hmac', 'SHA256' ), |
||
| 56 | 'HS512' => array( 'hash_hmac', 'SHA512' ), |
||
| 57 | 'HS384' => array( 'hash_hmac', 'SHA384' ), |
||
| 58 | 'RS256' => array( 'openssl', 'SHA256' ), |
||
| 59 | 'RS384' => array( 'openssl', 'SHA384' ), |
||
| 60 | 'RS512' => array( 'openssl', 'SHA512' ), |
||
| 61 | ); |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Decodes a JWT string into a PHP object. |
||
| 65 | * |
||
| 66 | * @param string $jwt The JWT. |
||
| 67 | * @param string|array $key The key, or map of keys. |
||
| 68 | * If the algorithm used is asymmetric, this is the public key. |
||
| 69 | * @param array $allowed_algs List of supported verification algorithms. |
||
| 70 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'. |
||
| 71 | * |
||
| 72 | * @return object The JWT's payload as a PHP object |
||
| 73 | * |
||
| 74 | * @throws UnexpectedValueException Provided JWT was invalid. |
||
| 75 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed. |
||
| 76 | * @throws InvalidArgumentException Provided JWT is trying to be used before it's eligible as defined by 'nbf'. |
||
| 77 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'. |
||
| 78 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim. |
||
| 79 | * |
||
| 80 | * @uses json_decode |
||
| 81 | * @uses urlsafe_b64_decode |
||
| 82 | */ |
||
| 83 | public static function decode( $jwt, $key, array $allowed_algs = array() ) { |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Converts and signs a PHP object or array into a JWT string. |
||
| 167 | * |
||
| 168 | * @param object|array $payload PHP object or array. |
||
| 169 | * @param string $key The secret key. |
||
| 170 | * If the algorithm used is asymmetric, this is the private key. |
||
| 171 | * @param string $alg The signing algorithm. |
||
| 172 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'. |
||
| 173 | * @param mixed $key_id The key ID. |
||
| 174 | * @param array $head An array with header elements to attach. |
||
| 175 | * |
||
| 176 | * @return string A signed JWT |
||
| 177 | * |
||
| 178 | * @uses json_encode |
||
| 179 | * @uses urlsafe_b64_decode |
||
| 180 | */ |
||
| 181 | public static function encode( $payload, $key, $alg = 'HS256', $key_id = null, $head = null ) { |
||
| 205 | |||
| 206 | /** |
||
| 207 | * Sign a string with a given key and algorithm. |
||
| 208 | * |
||
| 209 | * @param string $msg The message to sign. |
||
| 210 | * @param string|resource $key The secret key. |
||
| 211 | * @param string $alg The signing algorithm. |
||
| 212 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'. |
||
| 213 | * |
||
| 214 | * @return string An encrypted message |
||
| 215 | * |
||
| 216 | * @throws DomainException Unsupported algorithm was specified. |
||
| 217 | */ |
||
| 218 | public static function sign( $msg, $key, $alg = 'HS256' ) { |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Verify a signature with the message, key and method. Not all methods |
||
| 239 | * are symmetric, so we must have a separate verify and sign method. |
||
| 240 | * |
||
| 241 | * @param string $msg The original message (header and body). |
||
| 242 | * @param string $signature The original signature. |
||
| 243 | * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key. |
||
| 244 | * @param string $alg The algorithm. |
||
| 245 | * |
||
| 246 | * @return bool |
||
| 247 | * |
||
| 248 | * @throws DomainException Invalid Algorithm or OpenSSL failure. |
||
| 249 | */ |
||
| 250 | private static function verify( $msg, $signature, $key, $alg ) { |
||
| 291 | |||
| 292 | /** |
||
| 293 | * Decode a JSON string into a PHP object. |
||
| 294 | * |
||
| 295 | * @param string $input JSON string. |
||
| 296 | * |
||
| 297 | * @return object Object representation of JSON string |
||
| 298 | * |
||
| 299 | * @throws DomainException Provided string was invalid JSON. |
||
| 300 | */ |
||
| 301 | public static function json_decode( $input ) { |
||
| 327 | |||
| 328 | /** |
||
| 329 | * Encode a PHP object into a JSON string. |
||
| 330 | * |
||
| 331 | * @param object|array $input A PHP object or array. |
||
| 332 | * |
||
| 333 | * @return string JSON representation of the PHP object or array. |
||
| 334 | * |
||
| 335 | * @throws DomainException Provided object could not be encoded to valid JSON. |
||
| 336 | */ |
||
| 337 | public static function json_encode( $input ) { |
||
| 348 | |||
| 349 | /** |
||
| 350 | * Decode a string with URL-safe Base64. |
||
| 351 | * |
||
| 352 | * @param string $input A Base64 encoded string. |
||
| 353 | * |
||
| 354 | * @return string A decoded string |
||
| 355 | */ |
||
| 356 | public static function urlsafe_b64_decode( $input ) { |
||
| 365 | |||
| 366 | /** |
||
| 367 | * Encode a string with URL-safe Base64. |
||
| 368 | * |
||
| 369 | * @param string $input The string you want encoded. |
||
| 370 | * |
||
| 371 | * @return string The base64 encode of what you passed in |
||
| 372 | */ |
||
| 373 | public static function urlsafe_b64_encode( $input ) { |
||
| 377 | |||
| 378 | /** |
||
| 379 | * Helper method to create a JSON error. |
||
| 380 | * |
||
| 381 | * @param int $errno An error number from json_last_error(). |
||
| 382 | * @throws DomainException . |
||
| 383 | * |
||
| 384 | * @return void |
||
| 385 | */ |
||
| 386 | private static function handle_json_error( $errno ) { |
||
| 400 | |||
| 401 | /** |
||
| 402 | * Get the number of bytes in cryptographic strings. |
||
| 403 | * |
||
| 404 | * @param string $str . |
||
| 405 | * |
||
| 406 | * @return int |
||
| 407 | */ |
||
| 408 | private static function safe_strlen( $str ) { |
||
| 414 | } |
||
| 415 | |||
| 442 |
Let’s assume you have a class which uses late-static binding:
}
The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the
getSomeVariable()on that sub-class, you will receive a runtime error:In the case above, it makes sense to update
SomeClassto useselfinstead: