Completed
Push — fix/comments-photon ( a2496c...6aeef6 )
by
unknown
09:42
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
function jetpack_photon_url_scheme( $url, $scheme ) {
251
	if ( ! in_array( $scheme, array( 'http', 'https', 'network_path' ) ) ) {
252
		if ( preg_match( '#^(https?:)?//#', $url ) ) {
253
			return $url;
254
		}
255
256
		$scheme = 'http';
257
	}
258
259
	if ( 'network_path' == $scheme ) {
260
		$scheme_slashes = '//';
261
	} else {
262
		$scheme_slashes = "$scheme://";
263
	}
264
265
	return preg_replace( '#^([a-z:]+)?//#i', $scheme_slashes, $url );
266
}
267
268
/**
269
 * A wrapper for PHP's parse_url, prepending assumed scheme for network path
270
 * URLs. PHP versions 5.4.6 and earlier do not correctly parse without scheme.
271
 *
272
 * @see http://php.net/manual/en/function.parse-url.php#refsect1-function.parse-url-changelog
273
 *
274
 * @param string $url The URL to parse
275
 * @param integer $component Retrieve specific URL component
276
 * @return mixed Result of parse_url
277
 */
278
function jetpack_photon_parse_url( $url, $component = -1 ) {
279
	if ( 0 === strpos( $url, '//' ) ) {
280
		$url = ( is_ssl() ? 'https:' : 'http:' ) . $url;
281
	}
282
283
	return parse_url( $url, $component );
284
}
285
286
add_filter( 'jetpack_photon_skip_for_url', 'jetpack_photon_banned_domains', 9, 4 );
287
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...
288
	$banned_domains = array(
289
		'http://chart.googleapis.com/',
290
		'https://chart.googleapis.com/',
291
		'http://chart.apis.google.com/',
292
		'http://graph.facebook.com/',
293
		'https://graph.facebook.com/',
294
	);
295
296
	foreach ( $banned_domains as $banned_domain ) {
297
		if ( wp_startswith( $image_url, $banned_domain ) )
298
			return true;
299
	}
300
301
	return $skip;
302
}
303
304
305
/**
306
 * Jetpack Photon - Support Text Widgets.
307
 *
308
 * @access public
309
 * @param string $content Content from text widget.
310
 * @return string
311
 */
312
function jetpack_photon_support_text_widgets( $content ) {
313
	if ( class_exists( 'Jetpack_Photon' ) && Jetpack::is_module_active( 'photon' ) ) {
314
		return Jetpack_Photon::filter_the_content( $content );
315
	}
316
	return $content;
317
}
318
add_filter( 'widget_text', 'jetpack_photon_support_text_widgets' );
319