PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.12.0
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.12.0
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / inc / url_replacer.php

url_replacer.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 3.12.0, at inc/url_replacer.php

319 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The class handles the url replacements.
5 *
6 * @package \Optml\Inc
7 * @author Optimole <friends@optimole.com>
8 */
9 final class Optml_Url_Replacer extends Optml_App_Replacer {
10
11 use Optml_Dam_Offload_Utils;
12 use Optml_Validator;
13 use Optml_Normalizer;
14
15 /**
16 * Cached object instance.
17 *
18 * @var Optml_Url_Replacer
19 */
20 protected static $instance = null;
21
22 /**
23 * Class instance method.
24 *
25 * @codeCoverageIgnore
26 * @static
27 * @return Optml_Url_Replacer
28 * @since 1.0.0
29 * @access public
30 */
31 public static function instance() {
32 if ( null === self::$instance ) {
33 self::$instance = new self();
34 add_action( 'optml_replacer_setup', [ self::$instance, 'init' ] );
35 }
36
37 return self::$instance;
38 }
39
40 /**
41 * A filter which turns a local url into an optimized CDN image url or an array of image urls.
42 *
43 * @param string|array $url The url which should be replaced.
44 *
45 * @return string|array Replaced url.
46 */
47 public function replace_option_url( $url ) {
48 if ( empty( $url ) ) {
49 return $url;
50 }
51
52 // $url might be an array or an json encoded array with urls.
53 if ( is_array( $url ) || filter_var( $url, FILTER_VALIDATE_URL ) === false ) {
54 $array = $url;
55 $encoded = false;
56
57 // it might a json encoded array
58 if ( is_string( $url ) ) {
59 $array = json_decode( $url, true );
60 $encoded = true;
61 }
62
63 // in case there is an array, apply it recursively.
64 if ( is_array( $array ) ) {
65 foreach ( $array as $index => $value ) {
66 $array[ $index ] = $this->replace_option_url( $value );
67 }
68
69 if ( $encoded ) {
70 return json_encode( $array );
71 }
72
73 return $array;
74 }
75
76 if ( filter_var( $url, FILTER_VALIDATE_URL ) === false ) {
77 return $url;
78 }
79 }
80
81 return apply_filters( 'optml_content_url', $url );
82 }
83
84 /**
85 * The initialize method.
86 */
87 public function init() {
88
89 add_filter( 'optml_replace_image', [ $this, 'build_url' ], 10, 2 );
90 parent::init();
91
92 Optml_Quality::$default_quality = $this->to_accepted_quality( $this->settings->get_quality() );
93 Optml_Image::$watermark = new Optml_Watermark( $this->settings->get_site_settings()['watermark'] );
94 Optml_Resize::$default_enlarge = apply_filters( 'optml_always_enlarge', false );
95 add_filter( 'optml_content_url', [ $this, 'build_url' ], 1, 2 );
96
97 }
98
99 /**
100 * Returns a signed image url authorized to be used in our CDN.
101 *
102 * @param string $url The url which should be signed.
103 * @param array $args Dimension params; Supports `width` and `height`.
104 *
105 * @return string
106 */
107 public function build_url(
108 $url, $args = [
109 'width' => 'auto',
110 'height' => 'auto',
111 ]
112 ) {
113 if ( apply_filters( 'optml_dont_replace_url', false, $url ) ) {
114 return $url;
115 }
116
117 $original_url = $url;
118
119 $is_slashed = strpos( $url, '\/' ) !== false;
120
121 // We do a little hack here, for json unicode chars we first replace them with html special chars,
122 // we then strip slashes to normalize the URL and last we convert html special chars back to get a clean URL
123 $url = $is_slashed ? html_entity_decode( stripslashes( preg_replace( '/\\\u([\da-fA-F]{4})/', '&#x\1;', $url ) ) ) : ( $url );
124 $is_uploaded = Optml_Media_Offload::is_not_processed_image( $url );
125 if ( $is_uploaded === true ) {
126 $attachment_id = [];
127 preg_match( '/\/' . Optml_Media_Offload::KEYS['not_processed_flag'] . '([^\/]*)\//', $url, $attachment_id );
128 if ( ! isset( $attachment_id[1] ) ) {
129 return $url;
130 }
131 $id_and_filename = [];
132 preg_match( '/\/(' . Optml_Media_Offload::KEYS['uploaded_flag'] . '.*)/', $url, $id_and_filename );
133 if ( isset( $id_and_filename[0] ) ) {
134 $id_and_filename = $id_and_filename[0];
135 }
136 $sizes = $this->parse_dimension_from_optimized_url( $url );
137 if ( isset( $sizes[0] ) && $sizes[0] !== false ) {
138 $args['width'] = $sizes[0];
139 }
140 if ( isset( $sizes[1] ) && $sizes[1] !== false ) {
141 $args['height'] = $sizes[1];
142 }
143 $unoptimized_url = false;
144 if ( $attachment_id[1] === 'media_cloud' ) {
145 $tmp_url = explode( '/', $id_and_filename, 3 );
146 if ( isset( $tmp_url[2] ) ) {
147 $unoptimized_url = $tmp_url[2];
148 }
149 } else {
150 $unoptimized_url = Optml_Media_Offload::get_original_url( (int) $attachment_id[1] );
151 }
152 if ( $unoptimized_url !== false ) {
153 $url = $unoptimized_url;
154 }
155 }
156 if ( strpos( $url, Optml_Config::$service_url ) !== false && ! $this->url_has_dam_flag( $url ) ) {
157 return $original_url;
158 }
159
160 if ( ! $this->can_replace_url( $url ) ) {
161 return $original_url;
162 }
163
164 // Remove any query strings that might affect conversion.
165 $url = strtok( $url, '?' );
166 $ext = $this->is_valid_mimetype_from_url( $url, self::$filters[ Optml_Settings::FILTER_TYPE_OPTIMIZE ][ Optml_Settings::FILTER_EXT ] );
167 if ( false === $ext ) {
168 return $original_url;
169 }
170
171 if ( isset( $args['quality'] ) && ! empty( $args['quality'] ) ) {
172 $args['quality'] = $this->to_accepted_quality( $args['quality'] );
173 }
174
175 if ( isset( $args['minify'] ) && ! empty( $args['minify'] ) ) {
176 $args['minify'] = $this->to_accepted_minify( $args['minify'] );
177 }
178
179 // this will authorize the image
180 if ( ! empty( $this->site_mappings ) ) {
181 $url = str_replace( array_keys( $this->site_mappings ), array_values( $this->site_mappings ), $url );
182 }
183 if ( substr( $url, 0, 2 ) === '//' ) {
184 $url = sprintf( '%s:%s', is_ssl() ? 'https' : 'http', $url );
185 }
186 $normalized_ext = strtolower( $ext );
187 if ( isset( Optml_Config::$image_extensions[ $normalized_ext ] ) ) {
188 $new_url = $this->normalize_image( $url, $original_url, $args, $is_uploaded, $normalized_ext );
189 if ( $is_uploaded ) {
190 $new_url = str_replace( '/' . $url, $id_and_filename, $new_url );
191 }
192 } else {
193 $new_url = ( new Optml_Asset( $url, $args, $this->active_cache_buster_assets, $this->is_css_minify_on, $this->is_js_minify_on ) )->get_url();
194 }
195 return $is_slashed ? addcslashes( $new_url, '/' ) : $new_url;
196 }
197
198 /**
199 * Apply extra normalization to image.
200 *
201 * @param string $url Image url.
202 * @param string $original_url Original image url.
203 * @param array $args Args.
204 *
205 * @return string
206 */
207 private function normalize_image( $url, $original_url, $args, $is_uploaded = false, $ext = '' ) {
208
209 $new_url = $this->strip_image_size_from_url( $url );
210
211 if ( $new_url !== $url || $is_uploaded === true ) {
212 if ( ! isset( $args['quality'] ) || $args['quality'] !== 'eco' ) {
213 if ( $is_uploaded === true ) {
214 $crop = false;
215 }
216 if ( $is_uploaded !== true ) {
217 list($args['width'], $args['height'], $crop) = $this->parse_dimensions_from_filename( $url );
218 }
219 if ( ! $crop ) {
220 $sizes2crop = self::size_to_crop();
221 $crop = isset( $sizes2crop[ $args['width'] . $args['height'] ] ) ? $sizes2crop[ $args['width'] . $args['height'] ] : false;
222 }
223 if ( $crop ) {
224 $args['resize'] = $this->to_optml_crop( $crop );
225 }
226 }
227 $url = $new_url;
228 }
229
230 if ( ! isset( $args['width'] ) ) {
231 $args['width'] = $this->limit_dimensions_enabled ? $this->limit_width : 'auto';
232 }
233 if ( ! isset( $args['height'] ) ) {
234 $args['height'] = $this->limit_dimensions_enabled ? $this->limit_height : 'auto';
235 }
236
237 $args['width'] = (int) $args['width'];
238 $args['height'] = (int) $args['height'];
239
240 if ( $this->limit_dimensions_enabled && $args['height'] !== 0 && $args['width'] !== 0 ) {
241 if ( $args['width'] > $this->limit_width || $args['height'] > $this->limit_height ) {
242 $scale = min( $this->limit_width / $args['width'], $this->limit_height / $args['height'] );
243
244 if ( $scale < 1 ) {
245 $args['width'] = (int) floor( $scale * $args['width'] );
246 $args['height'] = (int) floor( $scale * $args['height'] );
247 }
248 }
249
250 if ( isset( $args['resize'], $args['resize']['enlarge'] ) ) {
251 $args['resize']['enlarge'] = false;
252 }
253 }
254
255 if ( isset( $args['resize'], $args['resize']['gravity'] ) && $this->settings->is_smart_cropping() ) {
256 $args['resize']['gravity'] = Optml_Resize::GRAVITY_SMART;
257 }
258
259 $args = apply_filters( 'optml_image_args', $args, $original_url );
260
261 $arguments = [
262 'apply_watermark' => apply_filters( 'optml_apply_watermark_for', true, $url ),
263 ];
264
265 if ( isset( $args['format'] ) && ! empty( $args['format'] ) ) {
266 $arguments['format'] = $args['format'];
267 }
268
269 // If format is not already set, we use best format if it's enabled.
270 if ( ! isset( $arguments['format'] ) && $this->settings->is_best_format() ) {
271 $arguments['format'] = 'best';
272 }
273
274 if ( $this->settings->get( 'strip_metadata' ) === 'disabled' ) {
275 $arguments['strip_metadata'] = '0';
276 }
277
278 if ( ! apply_filters( 'optml_should_avif_ext', true, $ext, $original_url ) ) {
279 $arguments['ignore_avif'] = true;
280 }
281
282 if ( ! isset( $arguments['ignore_avif'] ) && $this->settings->get( 'avif' ) === 'disabled' ) {
283 $arguments['ignore_avif'] = true;
284 }
285
286 return ( new Optml_Image( $url, $args, $this->active_cache_buster ) )->get_url( $arguments );
287
288 }
289
290 /**
291 * Throw error on object clone
292 *
293 * The whole idea of the singleton design pattern is that there is a single
294 * object therefore, we don't want the object to be cloned.
295 *
296 * @codeCoverageIgnore
297 * @access public
298 * @return void
299 * @since 1.0.0
300 */
301 public function __clone() {
302 // Cloning instances of the class is forbidden.
303 _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'optimole-wp' ), '1.0.0' );
304 }
305
306 /**
307 * Disable unserializing of the class
308 *
309 * @codeCoverageIgnore
310 * @access public
311 * @return void
312 * @since 1.0.0
313 */
314 public function __wakeup() {
315 // Unserializing instances of the class is forbidden.
316 _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'optimole-wp' ), '1.0.0' );
317 }
318 }
319