Completed
Pull Request — feature/sync-json-endpoints (#7037)
by
unknown
23:10 queued 11:24
created

functions.photon.php (2 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Generates a Photon URL.
5
 *
6
 * @see http://developer.wordpress.com/docs/photon/
7
 *
8
 * @param string $image_url URL to the publicly accessible image you want to manipulate
9
 * @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)
10
 * @return string The raw final URL. You should run this through esc_url() before displaying it.
11
 */
12
function jetpack_photon_url( $image_url, $args = array(), $scheme = null ) {
13
	$image_url = trim( $image_url );
14
15
	if ( class_exists( 'Jetpack') ) {
16
		/**
17
		 * Disables Photon URL processing for local development
18
	 	 *
19
	 	 * @module photon
20
	 	 *
21
	 	 * @since 4.1.0
22
	 	 *
23
	 	 * @param bool false Result of Jetpack::is_development_mode.
24
	 	 */
25
		if ( true === apply_filters( 'jetpack_photon_development_mode', Jetpack::is_development_mode() ) ) {
26
			return $image_url;
27
		}
28
	}
29
30
	/**
31
	 * Allow specific image URls to avoid going through Photon.
32
	 *
33
	 * @module photon
34
	 *
35
	 * @since 3.2.0
36
	 *
37
	 * @param bool false Should the image be returned as is, without going through Photon. Default to false.
38
	 * @param string $image_url Image URL.
39
	 * @param array|string $args Array of Photon arguments.
40
	 * @param string|null $scheme Image scheme. Default to null.
41
	 */
42
	if ( false !== apply_filters( 'jetpack_photon_skip_for_url', false, $image_url, $args, $scheme ) ) {
43
		return $image_url;
44
	}
45
46
	/**
47
	 * Filter the original image URL before it goes through Photon.
48
	 *
49
	 * @module photon
50
	 *
51
	 * @since 1.9.0
52
	 *
53
	 * @param string $image_url Image URL.
54
	 * @param array|string $args Array of Photon arguments.
55
	 * @param string|null $scheme Image scheme. Default to null.
56
	 */
57
	$image_url = apply_filters( 'jetpack_photon_pre_image_url', $image_url, $args, $scheme );
58
	/**
59
	 * Filter the original Photon image parameters before Photon is applied to an image.
60
	 *
61
	 * @module photon
62
	 *
63
	 * @since 1.9.0
64
	 *
65
	 * @param array|string $args Array of Photon arguments.
66
	 * @param string $image_url Image URL.
67
	 * @param string|null $scheme Image scheme. Default to null.
68
	 */
69
	$args = apply_filters( 'jetpack_photon_pre_args', $args, $image_url, $scheme );
70
71
	if ( empty( $image_url ) )
72
		return $image_url;
73
74
	$image_url_parts = @jetpack_photon_parse_url( $image_url );
75
76
	// Unable to parse
77
	if ( ! is_array( $image_url_parts ) || empty( $image_url_parts['host'] ) || empty( $image_url_parts['path'] ) )
78
		return $image_url;
79
80
	if ( is_array( $args ) ){
81
		// Convert values that are arrays into strings
82
		foreach ( $args as $arg => $value ) {
83
			if ( is_array( $value ) ) {
84
				$args[$arg] = implode( ',', $value );
85
			}
86
		}
87
88
		// Encode values
89
		// See http://core.trac.wordpress.org/ticket/17923
90
		$args = rawurlencode_deep( $args );
91
	}
92
93
	/* Don't photon-ize WPCOM hosted images with only the following url args:
94
	 *  `w`, `h`, `fit`, `crop`, `zoom`, `strip`, `resize`, `quality`
95
	 * These args can just be added to the wpcom-version of the image, and save on latency.
96
	 */
97
	$is_wpcom_image_with_safe_args = false;
98
	$allowed_wpcom_keys = array(
99
		'w'       => true,
100
		'h'       => true,
101
		'fit'     => true,
102
		'crop'    => true,
103
		'zoom'    => true,
104
		'strip'   => true,
105
		'resize'  => true,
106
		'quality' => true,
107
	);
108
	if ( wp_endswith( strtolower( $image_url_parts['host'] ), '.files.wordpress.com' ) && ! array_diff_key( $args, $allowed_wpcom_keys ) ) {
109
		$is_wpcom_image_with_safe_args = true;
110
	}
111
112
	/** This filter is documented below. */
113
	$custom_photon_url = apply_filters( 'jetpack_photon_domain', '', $image_url );
114
	$custom_photon_url = esc_url( $custom_photon_url );
115
116
	// You can't run a Photon URL through Photon again because query strings are stripped.
117
	// So if the image is already a Photon URL, append the new arguments to the existing URL.
118
	// Alternately, if it's a *.files.wordpress.com url, and the arguments are supported, keep the domain.
119
	if (
120
		in_array( $image_url_parts['host'], array( 'i0.wp.com', 'i1.wp.com', 'i2.wp.com' ) )
121
		|| $image_url_parts['host'] === jetpack_photon_parse_url( $custom_photon_url, PHP_URL_HOST )
122
		|| $is_wpcom_image_with_safe_args
123
	) {
124
		$photon_url = add_query_arg( $args, $image_url );
125
		return jetpack_photon_url_scheme( $photon_url, $scheme );
126
	}
127
128
	/**
129
	 * Allow Photon to use query strings as well.
130
	 * By default, Photon doesn't support query strings so we ignore them and look only at the path.
131
	 * This setting is Photon Server dependent.
132
	 *
133
	 * @module photon
134
	 *
135
	 * @since 1.9.0
136
	 *
137
	 * @param bool false Should images using query strings go through Photon. Default is false.
138
	 * @param string $image_url_parts['host'] Image URL's host.
139
	 */
140
	if ( ! apply_filters( 'jetpack_photon_any_extension_for_domain', false, $image_url_parts['host'] ) ) {
141
		// Photon doesn't support query strings so we ignore them and look only at the path.
142
		// However some source images are served via PHP so check the no-query-string extension.
143
		// For future proofing, this is a blacklist of common issues rather than a whitelist.
144
		$extension = pathinfo( $image_url_parts['path'], PATHINFO_EXTENSION );
145
		if ( empty( $extension ) || in_array( $extension, array( 'php', 'ashx' ) ) )
146
			return $image_url;
147
	}
148
149
	$image_host_path = $image_url_parts['host'] . $image_url_parts['path'];
150
151
	// Figure out which CDN subdomain to use
152
	srand( crc32( $image_host_path ) );
153
	$subdomain = rand( 0, 2 );
154
	srand();
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://i{$subdomain}.wp.com Domain used by Photon. $subdomain is a random number between 0 and 2.
164
	 * @param string $image_url URL of the image to be photonized.
165
	 */
166
	$photon_domain = apply_filters( 'jetpack_photon_domain', "https://i{$subdomain}.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
add_filter( 'jetpack_photon_url', 'jetpack_photon_url', 10, 3 );
202
203
/**
204
 * WordPress.com
205
 *
206
 * If a cropped WP.com-hosted image is the source image, have Photon replicate the crop.
207
 */
208
add_filter( 'jetpack_photon_pre_args', 'jetpack_photon_parse_wpcom_query_args', 10, 2 );
209
210
function jetpack_photon_parse_wpcom_query_args( $args, $image_url ) {
211
	$parsed_url = @parse_url( $image_url );
212
213
	if ( ! $parsed_url )
214
		return $args;
215
216
	$image_url_parts = wp_parse_args( $parsed_url, array(
217
		'host'  => '',
218
		'query' => ''
219
	) );
220
221
	if ( '.files.wordpress.com' != substr( $image_url_parts['host'], -20 ) )
222
		return $args;
223
224
	if ( empty( $image_url_parts['query'] ) )
225
		return $args;
226
227
	$wpcom_args = wp_parse_args( $image_url_parts['query'] );
228
229
	if ( empty( $wpcom_args['w'] ) || empty( $wpcom_args['h'] ) )
230
		return $args;
231
232
	// Keep the crop by using "resize"
233
	if ( ! empty( $wpcom_args['crop'] ) ) {
234 View Code Duplication
		if ( is_array( $args ) ) {
235
			$args = array_merge( array( 'resize' => array( $wpcom_args['w'], $wpcom_args['h'] ) ), $args );
236
		} else {
237
			$args = 'resize=' . rawurlencode( absint( $wpcom_args['w'] ) . ',' . absint( $wpcom_args['h'] ) ) . '&' . $args;
238
		}
239 View Code Duplication
	} else {
240
		if ( is_array( $args ) ) {
241
			$args = array_merge( array( 'fit' => array( $wpcom_args['w'], $wpcom_args['h'] ) ), $args );
242
		} else {
243
			$args = 'fit=' . rawurlencode( absint( $wpcom_args['w'] ) . ',' . absint( $wpcom_args['h'] ) ) . '&' . $args;
244
		}
245
	}
246
247
	return $args;
248
}
249
250
251
/**
252
 * Facebook
253
 */
254
add_filter( 'jetpack_photon_add_query_string_to_domain', 'jetpack_photon_allow_facebook_graph_domain', 10, 2 );
255
add_filter( 'jetpack_photon_any_extension_for_domain',   'jetpack_photon_allow_facebook_graph_domain', 10, 2 );
256
257
function jetpack_photon_url_scheme( $url, $scheme ) {
258
	if ( ! in_array( $scheme, array( 'http', 'https', 'network_path' ) ) ) {
259
		if ( preg_match( '#^(https?:)?//#', $url ) ) {
260
			return $url;
261
		}
262
263
		$scheme = 'http';
264
	}
265
266
	if ( 'network_path' == $scheme ) {
267
		$scheme_slashes = '//';
268
	} else {
269
		$scheme_slashes = "$scheme://";
270
	}
271
272
	return preg_replace( '#^([a-z:]+)?//#i', $scheme_slashes, $url );
273
}
274
275
function jetpack_photon_allow_facebook_graph_domain( $allow = false, $domain ) {
276
	switch ( $domain ) {
277
	case 'graph.facebook.com' :
278
		return true;
279
	}
280
281
	return $allow;
282
}
283
284
/**
285
 * A wrapper for PHP's parse_url, prepending assumed scheme for network path
286
 * URLs. PHP versions 5.4.6 and earlier do not correctly parse without scheme.
287
 *
288
 * @see http://php.net/manual/en/function.parse-url.php#refsect1-function.parse-url-changelog
289
 *
290
 * @param string $url The URL to parse
291
 * @param integer $component Retrieve specific URL component
292
 * @return mixed Result of parse_url
293
 */
294
function jetpack_photon_parse_url( $url, $component = -1 ) {
295
	if ( 0 === strpos( $url, '//' ) ) {
296
		$url = ( is_ssl() ? 'https:' : 'http:' ) . $url;
297
	}
298
299
	return parse_url( $url, $component );
300
}
301
302
add_filter( 'jetpack_photon_skip_for_url', 'jetpack_photon_banned_domains', 9, 4 );
303
function jetpack_photon_banned_domains( $skip, $image_url, $args, $scheme ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $scheme is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
304
	$banned_domains = array(
305
		'http://chart.googleapis.com/',
306
		'https://chart.googleapis.com/',
307
		'http://chart.apis.google.com/',
308
	);
309
310
	foreach ( $banned_domains as $banned_domain ) {
311
		if ( wp_startswith( $image_url, $banned_domain ) )
312
			return true;
313
	}
314
315
	return $skip;
316
}
317
318
319
/**
320
 * Jetpack Photon - Support Text Widgets.
321
 * 
322
 * @access public
323
 * @param string $content Content from text widget.
324
 * @return string
325
 */
326
function jetpack_photon_support_text_widgets( $content ) {
327
	if ( class_exists( 'Jetpack_Photon' ) && Jetpack::is_module_active( 'photon' ) ) {
328
		return Jetpack_Photon::filter_the_content( $content );
329
	}
330
	return $content;
331
}
332
add_filter( 'widget_text', 'jetpack_photon_support_text_widgets' );
333