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

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