| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | +use Optimole\Sdk\Resource\ImageProperty\ResizeTypeProperty; | |
| 4 | +use Optimole\Sdk\ValueObject\Position; | |
| 5 | + | |
| 3 | 6 | /** |
| 4 | 7 | * Normalization traits. |
| 5 | 8 | * |
| 6 | 9 | * @package \Optml\Inc\Traits |
| @@ -8,8 +11,15 @@ | ||
| 8 | 11 | */ |
| 9 | 12 | trait Optml_Normalizer { |
| 10 | 13 | |
| 11 | 14 | /** |
| 15 | + * Static cache for dimension calculations | |
| 16 | + * | |
| 17 | + * @var array | |
| 18 | + */ | |
| 19 | + private static $dimension_cache = []; | |
| 20 | + | |
| 21 | + /** | |
| 12 | 22 | * Normalize value to boolean. |
| 13 | 23 | * |
| 14 | 24 | * @param mixed $value Value to process. |
| 15 | 25 | * |
| @@ -15,13 +25,13 @@ | ||
| 15 | 25 | * |
| 16 | 26 | * @return bool |
| 17 | 27 | */ |
| 18 | 28 | public function to_boolean( $value ) { |
| 19 | - if ( in_array( $value, array( 'yes', 'enabled', 'true', '1' ) ) ) { | |
| 29 | + if ( in_array( $value, [ 'yes', 'enabled', 'true', '1' ], true ) ) { | |
| 20 | 30 | return true; |
| 21 | 31 | } |
| 22 | 32 | |
| 23 | - if ( in_array( $value, array( 'no', 'disabled', 'false', '0' ) ) ) { | |
| 33 | + if ( in_array( $value, [ 'no', 'disabled', 'false', '0' ], true ) ) { | |
| 24 | 34 | return false; |
| 25 | 35 | } |
| 26 | 36 | |
| 27 | 37 | return boolval( $value ); |
| @@ -27,37 +37,63 @@ | ||
| 27 | 37 | return boolval( $value ); |
| 28 | 38 | } |
| 29 | 39 | |
| 30 | 40 | /** |
| 31 | - * Strip slashes on unicode encoded strings. | |
| 41 | + * Get the unoptimized url from an Optimole url. | |
| 42 | + * Works only on non-offloaded images. | |
| 32 | 43 | * |
| 33 | - * @param string $string Input string. | |
| 44 | + * @param string $url The url to get the unoptimized url for. | |
| 34 | 45 | * |
| 35 | - * @return string Decoded string. | |
| 46 | + * @return string The unoptimized url. | |
| 36 | 47 | */ |
| 37 | - public function strip_slashes( $string ) { | |
| 38 | - return html_entity_decode( stripslashes( preg_replace( '/\\\u([\da-fA-F]{4})/', '&#x\1;', $string ) ) ); | |
| 48 | + public function get_unoptimized_url( $url ) { | |
| 49 | + | |
| 50 | + // If the url is not an optimole url, return the url | |
| 51 | + if ( strpos( $url, Optml_Config::$service_url ) === false ) { | |
| 52 | + return $url; | |
| 53 | + } | |
| 54 | + // If the url is an uploaded image, return the url | |
| 55 | + if ( Optml_Media_Offload::is_uploaded_image( $url ) ) { | |
| 56 | + $pattern = '#/id:([^/]+)/((?:https?://|http://|directUpload/)\S+)#'; | |
| 57 | + if ( preg_match( $pattern, $url, $matches ) ) { | |
| 58 | + $url = $matches[0]; | |
| 59 | + } | |
| 60 | + return $url; | |
| 61 | + } | |
| 62 | + | |
| 63 | + $url_parts = explode( 'http', $url ); | |
| 64 | + if ( ! isset( $url_parts[2] ) ) { | |
| 65 | + return $url; | |
| 66 | + } | |
| 67 | + return 'http' . $url_parts[2]; | |
| 39 | 68 | } |
| 40 | 69 | /** |
| 41 | - * Normalize value to an integer within bounds. | |
| 70 | + * Return domain hash. | |
| 42 | 71 | * |
| 43 | - * @param mixed $value Value to process. | |
| 44 | - * @param integer $min Lower bound. | |
| 45 | - * @param integer $max Upper bound. | |
| 72 | + * @param string $domain Full url. | |
| 46 | 73 | * |
| 47 | - * @return integer | |
| 74 | + * @return string Domain hash. | |
| 48 | 75 | */ |
| 49 | - public function to_bound_integer( $value, $min, $max ) { | |
| 50 | - $integer = absint( $value ); | |
| 51 | - if ( $integer < $min ) { | |
| 52 | - $integer = $min; | |
| 76 | + public function to_domain_hash( $domain ) { | |
| 77 | + $domain_parts = parse_url( $domain ); | |
| 78 | + $domain = isset( $domain_parts['host'] ) ? $domain_parts['host'] : ''; | |
| 79 | + $prefix = substr( $domain, 0, 4 ); | |
| 80 | + if ( $prefix === 'www.' ) { | |
| 81 | + $domain = substr( $domain, 4 ); | |
| 53 | 82 | } |
| 54 | - if ( $integer > $max ) { | |
| 55 | - $integer = $max; | |
| 56 | - } | |
| 57 | 83 | |
| 58 | - return $integer; | |
| 84 | + return base64_encode( $domain ); | |
| 59 | 85 | } |
| 86 | + /** | |
| 87 | + * Strip slashes on unicode encoded strings. | |
| 88 | + * | |
| 89 | + * @param string $content Input string. | |
| 90 | + * | |
| 91 | + * @return string Decoded string. | |
| 92 | + */ | |
| 93 | + public function strip_slashes( $content ) { | |
| 94 | + return html_entity_decode( stripslashes( preg_replace( '/\\\u([\da-fA-F]{4})/', '&#x\1;', $content ) ) ); | |
| 95 | + } | |
| 60 | 96 | |
| 61 | 97 | /** |
| 62 | 98 | * Normalize value to positive integer. |
| 63 | 99 | * |
| @@ -75,18 +111,18 @@ | ||
| 75 | 111 | * Normalize value to map. |
| 76 | 112 | * |
| 77 | 113 | * @param mixed $value Value to process. |
| 78 | 114 | * @param array $map Associative list from witch to return. |
| 79 | - * @param mixed $default Default. | |
| 115 | + * @param mixed $initial Default. | |
| 80 | 116 | * |
| 81 | 117 | * @return mixed |
| 82 | 118 | */ |
| 83 | - public function to_map_values( $value, $map, $default ) { | |
| 84 | - if ( in_array( $value, $map ) ) { | |
| 119 | + public function to_map_values( $value, $map, $initial ) { | |
| 120 | + if ( in_array( $value, $map, true ) ) { | |
| 85 | 121 | return $value; |
| 86 | 122 | } |
| 87 | 123 | |
| 88 | - return $default; | |
| 124 | + return $initial; | |
| 89 | 125 | } |
| 90 | 126 | |
| 91 | 127 | /** |
| 92 | 128 | * Normalize value to an accepted quality. |
| @@ -100,15 +136,16 @@ | ||
| 100 | 136 | return intval( $value ); |
| 101 | 137 | } |
| 102 | 138 | $value = trim( $value ); |
| 103 | 139 | |
| 104 | - $accepted_qualities = array( | |
| 140 | + $accepted_qualities = [ | |
| 105 | 141 | 'eco' => 'eco', |
| 106 | 142 | 'auto' => 'auto', |
| 143 | + 'mauto' => 'mauto', | |
| 107 | 144 | 'high_c' => 55, |
| 108 | 145 | 'medium_c' => 75, |
| 109 | 146 | 'low_c' => 90, |
| 110 | - ); | |
| 147 | + ]; | |
| 111 | 148 | |
| 112 | 149 | if ( array_key_exists( $value, $accepted_qualities ) ) { |
| 113 | 150 | return $accepted_qualities[ $value ]; |
| 114 | 151 | } |
| @@ -132,30 +169,122 @@ | ||
| 132 | 169 | return 'auto'; |
| 133 | 170 | } |
| 134 | 171 | |
| 135 | 172 | /** |
| 173 | + * Normalize value to an integer within bounds. | |
| 174 | + * | |
| 175 | + * @param mixed $value Value to process. | |
| 176 | + * @param integer $min Lower bound. | |
| 177 | + * @param integer $max Upper bound. | |
| 178 | + * | |
| 179 | + * @return integer | |
| 180 | + */ | |
| 181 | + public function to_bound_integer( $value, $min, $max ) { | |
| 182 | + $integer = absint( $value ); | |
| 183 | + if ( $integer < $min ) { | |
| 184 | + $integer = $min; | |
| 185 | + } | |
| 186 | + if ( $integer > $max ) { | |
| 187 | + $integer = $max; | |
| 188 | + } | |
| 189 | + | |
| 190 | + return $integer; | |
| 191 | + } | |
| 192 | + | |
| 193 | + /** | |
| 194 | + * Convert image size to dimensions. | |
| 195 | + * | |
| 196 | + * This function takes an image size and its metadata, and returns the dimensions | |
| 197 | + * of the image based on the specified size. It handles different cases such as | |
| 198 | + * custom sizes, predefined sizes, and full size. | |
| 199 | + * | |
| 200 | + * @param mixed $size The size of the image. Can be an array of width and height, a predefined size, or 'full'. | |
| 201 | + * @param array $image_meta Metadata of the image, including width and height. | |
| 202 | + * @param int $attachment_id The ID of the attachment. | |
| 203 | + * | |
| 204 | + * @return array The dimensions of the image, including width, height, and optional resize parameters. | |
| 205 | + */ | |
| 206 | + public function size_to_dimension( $size, $image_meta, $attachment_id = null ) { | |
| 207 | + // default size | |
| 208 | + $sizes = [ | |
| 209 | + 'width' => isset( $image_meta['width'] ) ? intval( $image_meta['width'] ) : false, | |
| 210 | + 'height' => isset( $image_meta['height'] ) ? intval( $image_meta['height'] ) : false, | |
| 211 | + ]; | |
| 212 | + $image_args = Optml_App_Replacer::image_sizes(); | |
| 213 | + $sizes2crop = Optml_App_Replacer::size_to_crop(); | |
| 214 | + switch ( $size ) { | |
| 215 | + case is_array( $size ): | |
| 216 | + $width = isset( $size[0] ) ? (int) $size[0] : false; | |
| 217 | + $height = isset( $size[1] ) ? (int) $size[1] : false; | |
| 218 | + if ( ! $width || ! $height ) { | |
| 219 | + break; | |
| 220 | + } | |
| 221 | + $cache_key = 'a' . $width . '_' . $height . '_' . $sizes['width'] . '_' . $sizes['height']; | |
| 222 | + if ( isset( self::$dimension_cache[ $cache_key ] ) ) { | |
| 223 | + return self::$dimension_cache[ $cache_key ]; | |
| 224 | + } | |
| 225 | + if ( $attachment_id ) { | |
| 226 | + $intermediate = image_get_intermediate_size( $attachment_id, $size ); | |
| 227 | + if ( $intermediate ) { | |
| 228 | + $sizes['width'] = $intermediate['width']; | |
| 229 | + $sizes['height'] = $intermediate['height']; | |
| 230 | + } | |
| 231 | + } | |
| 232 | + | |
| 233 | + list( $sizes['width'], $sizes['height'] ) = image_constrain_size_for_editor( $sizes['width'], $sizes['height'], $size ); | |
| 234 | + $resize = apply_filters( 'optml_default_crop', [] ); | |
| 235 | + if ( isset( $sizes2crop[ $sizes['width'] . $sizes['height'] ] ) ) { | |
| 236 | + $resize = $this->to_optml_crop( $sizes2crop[ $sizes['width'] . $sizes['height'] ] ); | |
| 237 | + } | |
| 238 | + $sizes['resize'] = $resize; | |
| 239 | + self::$dimension_cache[ $cache_key ] = $sizes; | |
| 240 | + break; | |
| 241 | + case 'full' !== $size && isset( $image_args[ $size ] ): | |
| 242 | + $cache_key = 'b' . $size . '_' . $sizes['width'] . '_' . $sizes['height']; | |
| 243 | + if ( isset( self::$dimension_cache[ $cache_key ] ) ) { | |
| 244 | + return self::$dimension_cache[ $cache_key ]; | |
| 245 | + } | |
| 246 | + $image_resized = image_resize_dimensions( $sizes['width'], $sizes['height'], $image_args[ $size ]['width'], $image_args[ $size ]['height'], $image_args[ $size ]['crop'] ); | |
| 247 | + | |
| 248 | + if ( $image_resized ) { // This could be false when the requested image size is larger than the full-size image. | |
| 249 | + $sizes['width'] = $image_resized[6]; | |
| 250 | + $sizes['height'] = $image_resized[7]; | |
| 251 | + } | |
| 252 | + // There are cases when the image meta is missing and image size is non existent, see SVG image handling. | |
| 253 | + if ( ! $sizes['width'] || ! $sizes['height'] ) { | |
| 254 | + break; | |
| 255 | + } | |
| 256 | + list( $sizes['width'], $sizes['height'] ) = image_constrain_size_for_editor( $sizes['width'], $sizes['height'], $size, 'display' ); | |
| 257 | + | |
| 258 | + $sizes['resize'] = $this->to_optml_crop( $image_args[ $size ]['crop'] ); | |
| 259 | + self::$dimension_cache[ $cache_key ] = $sizes; | |
| 260 | + break; | |
| 261 | + } | |
| 262 | + return $sizes; | |
| 263 | + } | |
| 264 | + /** | |
| 136 | 265 | * Normalize arguments for crop. |
| 137 | 266 | * |
| 138 | - * @param array $crop_args Crop arguments. | |
| 267 | + * @param array|bool $crop_args Crop arguments. | |
| 139 | 268 | * |
| 140 | 269 | * @return array |
| 141 | 270 | */ |
| 142 | - public function to_optml_crop( $crop_args = array() ) { | |
| 271 | + public function to_optml_crop( $crop_args = [] ) { | |
| 143 | 272 | |
| 144 | 273 | $enlarge = false; |
| 145 | 274 | if ( isset( $crop_args['enlarge'] ) ) { |
| 275 | + $enlarge = $crop_args['enlarge']; | |
| 146 | 276 | $crop_args = $crop_args['crop']; |
| 147 | - $enlarge = $crop_args['enlarge']; | |
| 148 | 277 | } |
| 149 | 278 | if ( $crop_args === true ) { |
| 150 | - return array( | |
| 151 | - 'type' => Optml_Resize::RESIZE_FILL, | |
| 279 | + return [ | |
| 280 | + 'type' => ResizeTypeProperty::FILL, | |
| 152 | 281 | 'enlarge' => $enlarge, |
| 153 | - 'gravity' => Optml_Resize::GRAVITY_CENTER, | |
| 154 | - ); | |
| 282 | + 'gravity' => Position::CENTER, | |
| 283 | + ]; | |
| 155 | 284 | } |
| 156 | - if ( $crop_args === false || ! is_array( $crop_args ) || count( $crop_args ) != 2 ) { | |
| 157 | - return array(); | |
| 285 | + if ( $crop_args === false || ! is_array( $crop_args ) || count( $crop_args ) !== 2 ) { | |
| 286 | + return []; | |
| 158 | 287 | } |
| 159 | 288 | |
| 160 | 289 | $allowed_x = [ |
| 161 | 290 | 'left' => true, |
| @@ -166,38 +295,38 @@ | ||
| 166 | 295 | 'top' => true, |
| 167 | 296 | 'center' => true, |
| 168 | 297 | 'bottom' => true, |
| 169 | 298 | ]; |
| 170 | - $allowed_gravities = array( | |
| 171 | - 'left' => Optml_Resize::GRAVITY_WEST, | |
| 172 | - 'right' => Optml_Resize::GRAVITY_EAST, | |
| 173 | - 'top' => Optml_Resize::GRAVITY_NORTH, | |
| 174 | - 'bottom' => Optml_Resize::GRAVITY_SOUTH, | |
| 175 | - 'lefttop' => Optml_Resize::GRAVITY_NORTH_WEST, | |
| 176 | - 'leftbottom' => Optml_Resize::GRAVITY_SOUTH_WEST, | |
| 177 | - 'righttop' => Optml_Resize::GRAVITY_NORTH_EAST, | |
| 178 | - 'rightbottom' => Optml_Resize::GRAVITY_SOUTH_EAST, | |
| 179 | - 'centertop' => array( 0.5, 0 ), | |
| 180 | - 'centerbottom' => array( 0.5, 1 ), | |
| 181 | - 'leftcenter' => array( 0, 0.5 ), | |
| 182 | - 'rightcenter' => array( 1, 0.5 ), | |
| 183 | - ); | |
| 299 | + $allowed_gravities = [ | |
| 300 | + 'left' => Position::WEST, | |
| 301 | + 'right' => Position::EAST, | |
| 302 | + 'top' => Position::NORTH, | |
| 303 | + 'bottom' => Position::SOUTH, | |
| 304 | + 'lefttop' => Position::NORTH_WEST, | |
| 305 | + 'leftbottom' => Position::SOUTH_WEST, | |
| 306 | + 'righttop' => Position::NORTH_EAST, | |
| 307 | + 'rightbottom' => Position::SOUTH_EAST, | |
| 308 | + 'centertop' => [ 0.5, 0 ], | |
| 309 | + 'centerbottom' => [ 0.5, 1 ], | |
| 310 | + 'leftcenter' => [ 0, 0.5 ], | |
| 311 | + 'rightcenter' => [ 1, 0.5 ], | |
| 312 | + ]; | |
| 184 | 313 | |
| 185 | - $gravity = Optml_Resize::GRAVITY_CENTER; | |
| 314 | + $gravity = Position::CENTER; | |
| 186 | 315 | $key_search = ( $crop_args[0] === true ? '' : |
| 187 | 316 | ( isset( $allowed_x[ $crop_args[0] ] ) ? $crop_args[0] : '' ) ) . |
| 188 | - ( $crop_args[1] === true ? '' : | |
| 189 | - ( isset( $allowed_y[ $crop_args[1] ] ) ? $crop_args[1] : '' ) ); | |
| 317 | + ( $crop_args[1] === true ? '' : | |
| 318 | + ( isset( $allowed_y[ $crop_args[1] ] ) ? $crop_args[1] : '' ) ); | |
| 190 | 319 | |
| 191 | 320 | if ( array_key_exists( $key_search, $allowed_gravities ) ) { |
| 192 | 321 | $gravity = $allowed_gravities[ $key_search ]; |
| 193 | 322 | } |
| 194 | 323 | |
| 195 | - return array( | |
| 196 | - 'type' => Optml_Resize::RESIZE_FILL, | |
| 324 | + return [ | |
| 325 | + 'type' => ResizeTypeProperty::FILL, | |
| 197 | 326 | 'enlarge' => $enlarge, |
| 198 | 327 | 'gravity' => $gravity, |
| 199 | - ); | |
| 328 | + ]; | |
| 200 | 329 | } |
| 201 | 330 | |
| 202 | 331 | /** |
| 203 | 332 | * Normalize arguments for watermark. |
| @@ -205,26 +334,46 @@ | ||
| 205 | 334 | * @param array $watermark_args Watermark arguments. |
| 206 | 335 | * |
| 207 | 336 | * @return array |
| 208 | 337 | */ |
| 209 | - public function to_optml_watermark( $watermark_args = array() ) { | |
| 210 | - $allowed_gravities = array( | |
| 211 | - 'left' => Optml_Resize::GRAVITY_WEST, | |
| 212 | - 'right' => Optml_Resize::GRAVITY_EAST, | |
| 213 | - 'top' => Optml_Resize::GRAVITY_NORTH, | |
| 214 | - 'bottom' => Optml_Resize::GRAVITY_SOUTH, | |
| 215 | - 'left_top' => Optml_Resize::GRAVITY_NORTH_WEST, | |
| 216 | - 'left_bottom' => Optml_Resize::GRAVITY_SOUTH_WEST, | |
| 217 | - 'right_top' => Optml_Resize::GRAVITY_NORTH_EAST, | |
| 218 | - 'right_bottom' => Optml_Resize::GRAVITY_SOUTH_EAST, | |
| 219 | - ); | |
| 220 | - $gravity = Optml_Resize::GRAVITY_CENTER; | |
| 338 | + public function to_optml_watermark( $watermark_args = [] ) { | |
| 339 | + $allowed_gravities = [ | |
| 340 | + 'left' => Position::WEST, | |
| 341 | + 'right' => Position::EAST, | |
| 342 | + 'top' => Position::NORTH, | |
| 343 | + 'bottom' => Position::SOUTH, | |
| 344 | + 'left_top' => Position::NORTH_WEST, | |
| 345 | + 'left_bottom' => Position::SOUTH_WEST, | |
| 346 | + 'right_top' => Position::NORTH_EAST, | |
| 347 | + 'right_bottom' => Position::SOUTH_EAST, | |
| 348 | + ]; | |
| 349 | + $gravity = Position::CENTER; | |
| 221 | 350 | if ( isset( $watermark_args['position'] ) && array_key_exists( $watermark_args['position'], $allowed_gravities ) ) { |
| 222 | 351 | $gravity = $allowed_gravities[ $watermark_args['position'] ]; |
| 223 | 352 | } |
| 224 | 353 | |
| 225 | - return array( | |
| 354 | + return [ | |
| 226 | 355 | 'opacity' => 1, |
| 227 | 356 | 'position' => $gravity, |
| 228 | - ); | |
| 357 | + ]; | |
| 358 | + } | |
| 359 | + /** | |
| 360 | + * If missing, add schema to urls. | |
| 361 | + * | |
| 362 | + * @param string $url Url to check. | |
| 363 | + * | |
| 364 | + * @return string | |
| 365 | + */ | |
| 366 | + public function add_schema( $url ) { | |
| 367 | + $schema_url = $url; | |
| 368 | + $should_add_schema = false; | |
| 369 | + if ( function_exists( 'str_starts_with' ) ) { | |
| 370 | + $should_add_schema = str_starts_with( $schema_url, '//' ); | |
| 371 | + } else { | |
| 372 | + $should_add_schema = substr( $schema_url, 0, strlen( '//' ) ) === '//'; | |
| 373 | + } | |
| 374 | + if ( $should_add_schema ) { | |
| 375 | + $schema_url = ( is_ssl() ? 'https:' : 'http:' ) . $schema_url; | |
| 376 | + } | |
| 377 | + return $schema_url; | |
| 229 | 378 | } |
| 230 | 379 | } |