PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.5.3
Jetpack – WP Security, Backup, Speed, & Growth v10.5.3
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / functions.photon.php
functions.photon.php
345 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Status;
4
5 /**
6 * Generic functions using the Photon service.
7 *
8 * Some are used outside of the Photon module being active, so intentionally not within the module.
9 *
10 * @package automattic/jetpack
11 */
12
13 /**
14 * Generates a Photon URL.
15 *
16 * @see https://developer.wordpress.com/docs/photon/
17 *
18 * @param string $image_url URL to the publicly accessible image you want to manipulate.
19 * @param array|string $args An array of arguments, i.e. array( 'w' => '300', 'resize' => array( 123, 456 ) ), or in string form (w=123&h=456).
20 * @param string|null $scheme URL protocol.
21 * @return string The raw final URL. You should run this through esc_url() before displaying it.
22 */
23 function jetpack_photon_url( $image_url, $args = array(), $scheme = null ) {
24 $image_url = trim( $image_url );
25
26 if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
27 /**
28 * Disables Photon URL processing for local development
29 *
30 * @module photon
31 *
32 * @since 4.1.0
33 *
34 * @param bool false Result of Automattic\Jetpack\Status->is_offline_mode().
35 */
36 if ( true === apply_filters( 'jetpack_photon_development_mode', ( new Status() )->is_offline_mode() ) ) {
37 return $image_url;
38 }
39 }
40
41 /**
42 * Allow specific image URls to avoid going through Photon.
43 *
44 * @module photon
45 *
46 * @since 3.2.0
47 *
48 * @param bool false Should the image be returned as is, without going through Photon. Default to false.
49 * @param string $image_url Image URL.
50 * @param array|string $args Array of Photon arguments.
51 * @param string|null $scheme Image scheme. Default to null.
52 */
53 if ( false !== apply_filters( 'jetpack_photon_skip_for_url', false, $image_url, $args, $scheme ) ) {
54 return $image_url;
55 }
56
57 /**
58 * Filter the original image URL before it goes through Photon.
59 *
60 * @module photon
61 *
62 * @since 1.9.0
63 *
64 * @param string $image_url Image URL.
65 * @param array|string $args Array of Photon arguments.
66 * @param string|null $scheme Image scheme. Default to null.
67 */
68 $image_url = apply_filters( 'jetpack_photon_pre_image_url', $image_url, $args, $scheme );
69 /**
70 * Filter the original Photon image parameters before Photon is applied to an image.
71 *
72 * @module photon
73 *
74 * @since 1.9.0
75 *
76 * @param array|string $args Array of Photon arguments.
77 * @param string $image_url Image URL.
78 * @param string|null $scheme Image scheme. Default to null.
79 */
80 $args = apply_filters( 'jetpack_photon_pre_args', $args, $image_url, $scheme );
81
82 if ( empty( $image_url ) ) {
83 return $image_url;
84 }
85
86 $image_url_parts = wp_parse_url( $image_url );
87
88 // Unable to parse.
89 if ( ! is_array( $image_url_parts ) || empty( $image_url_parts['host'] ) || empty( $image_url_parts['path'] ) ) {
90 return $image_url;
91 }
92
93 if ( is_array( $args ) ) {
94 // Convert values that are arrays into strings.
95 foreach ( $args as $arg => $value ) {
96 if ( is_array( $value ) ) {
97 $args[ $arg ] = implode( ',', $value );
98 }
99 }
100
101 // Encode values.
102 // See https://core.trac.wordpress.org/ticket/17923 .
103 $args = rawurlencode_deep( $args );
104 }
105
106 // Don't photon-ize WPCOM hosted images -- we can serve them up from wpcom directly.
107 $is_wpcom_image = false;
108 if ( wp_endswith( strtolower( $image_url_parts['host'] ), '.files.wordpress.com' ) ) {
109 $is_wpcom_image = true;
110 if ( isset( $args['ssl'] ) ) {
111 // Do not send the ssl argument to prevent caching issues.
112 unset( $args['ssl'] );
113 }
114 }
115
116 /** This filter is documented below. */
117 $custom_photon_url = apply_filters( 'jetpack_photon_domain', '', $image_url );
118 $custom_photon_url = esc_url( $custom_photon_url );
119
120 // You can't run a Photon URL through Photon again because query strings are stripped.
121 // So if the image is already a Photon URL, append the new arguments to the existing URL.
122 // Alternately, if it's a *.files.wordpress.com url, then keep the domain as is.
123 if (
124 in_array( $image_url_parts['host'], array( 'i0.wp.com', 'i1.wp.com', 'i2.wp.com' ), true )
125 || wp_parse_url( $custom_photon_url, PHP_URL_HOST ) === $image_url_parts['host']
126 || $is_wpcom_image
127 ) {
128 $photon_url = add_query_arg( $args, $image_url );
129 return jetpack_photon_url_scheme( $photon_url, $scheme );
130 }
131
132 /**
133 * Allow Photon to use query strings as well.
134 * By default, Photon doesn't support query strings so we ignore them and look only at the path.
135 * This setting is Photon Server dependent.
136 *
137 * @module photon
138 *
139 * @since 1.9.0
140 *
141 * @param bool false Should images using query strings go through Photon. Default is false.
142 * @param string $image_url_parts['host'] Image URL's host.
143 */
144 if ( ! apply_filters( 'jetpack_photon_any_extension_for_domain', false, $image_url_parts['host'] ) ) {
145 // Photon doesn't support query strings so we ignore them and look only at the path.
146 // However some source images are served via PHP so check the no-query-string extension.
147 // For future proofing, this is an excluded list of common issues rather than an allow list.
148 $extension = pathinfo( $image_url_parts['path'], PATHINFO_EXTENSION );
149 if ( empty( $extension ) || in_array( $extension, array( 'php', 'ashx' ), true ) ) {
150 return $image_url;
151 }
152 }
153
154 $image_host_path = $image_url_parts['host'] . $image_url_parts['path'];
155
156 /**
157 * Filters the domain used by the Photon module.
158 *
159 * @module photon
160 *
161 * @since 3.4.2
162 *
163 * @param string https://i0.wp.com Domain used by Photon.
164 * @param string $image_url URL of the image to be photonized.
165 */
166 $photon_domain = apply_filters( 'jetpack_photon_domain', 'https://i0.wp.com', $image_url );
167 $photon_domain = trailingslashit( esc_url( $photon_domain ) );
168 $photon_url = $photon_domain . $image_host_path;
169
170 /**
171 * Add query strings to Photon URL.
172 * By default, Photon doesn't support query strings so we ignore them.
173 * This setting is Photon Server dependent.
174 *
175 * @module photon
176 *
177 * @since 1.9.0
178 *
179 * @param bool false Should query strings be added to the image URL. Default is false.
180 * @param string $image_url_parts['host'] Image URL's host.
181 */
182 if ( isset( $image_url_parts['query'] ) && apply_filters( 'jetpack_photon_add_query_string_to_domain', false, $image_url_parts['host'] ) ) {
183 $photon_url .= '?q=' . rawurlencode( $image_url_parts['query'] );
184 }
185
186 if ( $args ) {
187 if ( is_array( $args ) ) {
188 $photon_url = add_query_arg( $args, $photon_url );
189 } else {
190 // You can pass a query string for complicated requests but where you still want CDN subdomain help, etc.
191 $photon_url .= '?' . $args;
192 }
193 }
194
195 if ( isset( $image_url_parts['scheme'] ) && 'https' === $image_url_parts['scheme'] ) {
196 $photon_url = add_query_arg( array( 'ssl' => 1 ), $photon_url );
197 }
198
199 return jetpack_photon_url_scheme( $photon_url, $scheme );
200 }
201
202 /**
203 * Add an easy way to photon-ize a URL that is safe to call even if Jetpack isn't active.
204 *
205 * See: https://jetpack.com/2013/07/11/photon-and-themes/
206 */
207 add_filter( 'jetpack_photon_url', 'jetpack_photon_url', 10, 3 );
208
209 /**
210 * WordPress.com
211 *
212 * If a cropped WP.com-hosted image is the source image, have Photon replicate the crop.
213 */
214 add_filter( 'jetpack_photon_pre_args', 'jetpack_photon_parse_wpcom_query_args', 10, 2 );
215
216 /**
217 * Parses WP.com-hosted image args to replicate the crop.
218 *
219 * @param mixed $args Args set during Photon's processing.
220 * @param string $image_url URL of the image.
221 * @return array|string Args for Photon to use for the URL.
222 */
223 function jetpack_photon_parse_wpcom_query_args( $args, $image_url ) {
224 $parsed_url = wp_parse_url( $image_url );
225
226 if ( ! $parsed_url ) {
227 return $args;
228 }
229
230 $image_url_parts = wp_parse_args(
231 $parsed_url,
232 array(
233 'host' => '',
234 'query' => '',
235 )
236 );
237
238 if ( '.files.wordpress.com' !== substr( $image_url_parts['host'], -20 ) ) {
239 return $args;
240 }
241
242 if ( empty( $image_url_parts['query'] ) ) {
243 return $args;
244 }
245
246 $wpcom_args = wp_parse_args( $image_url_parts['query'] );
247
248 if ( empty( $wpcom_args['w'] ) || empty( $wpcom_args['h'] ) ) {
249 return $args;
250 }
251
252 // Keep the crop by using "resize".
253 if ( ! empty( $wpcom_args['crop'] ) ) {
254 if ( is_array( $args ) ) {
255 $args = array_merge( array( 'resize' => array( $wpcom_args['w'], $wpcom_args['h'] ) ), $args );
256 } else {
257 $args = 'resize=' . rawurlencode( absint( $wpcom_args['w'] ) . ',' . absint( $wpcom_args['h'] ) ) . '&' . $args;
258 }
259 } else {
260 if ( is_array( $args ) ) {
261 $args = array_merge( array( 'fit' => array( $wpcom_args['w'], $wpcom_args['h'] ) ), $args );
262 } else {
263 $args = 'fit=' . rawurlencode( absint( $wpcom_args['w'] ) . ',' . absint( $wpcom_args['h'] ) ) . '&' . $args;
264 }
265 }
266
267 return $args;
268 }
269
270 /**
271 * Sets the scheme for a URL
272 *
273 * @param string $url URL to set scheme.
274 * @param string $scheme Scheme to use. Accepts http, https, network_path.
275 *
276 * @return string URL.
277 */
278 function jetpack_photon_url_scheme( $url, $scheme ) {
279 if ( ! in_array( $scheme, array( 'http', 'https', 'network_path' ), true ) ) {
280 if ( preg_match( '#^(https?:)?//#', $url ) ) {
281 return $url;
282 }
283
284 $scheme = 'http';
285 }
286
287 if ( 'network_path' === $scheme ) {
288 $scheme_slashes = '//';
289 } else {
290 $scheme_slashes = "$scheme://";
291 }
292
293 return preg_replace( '#^([a-z:]+)?//#i', $scheme_slashes, $url );
294 }
295
296 add_filter( 'jetpack_photon_skip_for_url', 'jetpack_photon_banned_domains', 9, 2 );
297
298 /**
299 * Check to skip Photon for a known domain that shouldn't be Photonized.
300 *
301 * @param bool $skip If the image should be skipped by Photon.
302 * @param string $image_url URL of the image.
303 *
304 * @return bool Should the image be skipped by Photon.
305 */
306 function jetpack_photon_banned_domains( $skip, $image_url ) {
307 $banned_host_patterns = array(
308 '/^chart\.googleapis\.com$/',
309 '/^chart\.apis\.google\.com$/',
310 '/^graph\.facebook\.com$/',
311 '/\.fbcdn\.net$/',
312 '/\.paypalobjects\.com$/',
313 '/\.dropbox\.com$/',
314 '/\.cdninstagram\.com$/',
315 '/^(commons|upload)\.wikimedia\.org$/',
316 '/\.wikipedia\.org$/',
317 );
318
319 $host = wp_parse_url( $image_url, PHP_URL_HOST );
320
321 foreach ( $banned_host_patterns as $banned_host_pattern ) {
322 if ( 1 === preg_match( $banned_host_pattern, $host ) ) {
323 return true;
324 }
325 }
326
327 return $skip;
328 }
329
330
331 /**
332 * Jetpack Photon - Support Text Widgets.
333 *
334 * @access public
335 * @param string $content Content from text widget.
336 * @return string
337 */
338 function jetpack_photon_support_text_widgets( $content ) {
339 if ( class_exists( 'Jetpack_Photon' ) && Jetpack::is_module_active( 'photon' ) ) {
340 return Jetpack_Photon::filter_the_content( $content );
341 }
342 return $content;
343 }
344 add_filter( 'widget_text', 'jetpack_photon_support_text_widgets' );
345