PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.10.0
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.10.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.10.0, at inc/url_replacer.php

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