Completed
Branch plugin-submission (ccb0d8)
by LA
01:53
created

do-functions.php ➔ imgix_add_retina()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 * @package imgix
5
 */
6
7
/**
8
 * Find all img tags with sources matching "imgix.net" without the parameter
9
 * "srcset" and add the "srcset" parameter to all those images, appending a new
10
 * source using the "dpr=2" modifier.
11
 *
12
 * @param $content
13
 * @return string Content with retina-enriched image tags.
14
 */
15
function imgix_add_retina( $content ) {
16
	$pattern = '/<img((?![^>]+srcset )([^>]*)';
17
	$pattern .= 'src=[\'"]([^\'"]*imgix.net[^\'"]*\?[^\'"]*w=[^\'"]*)[\'"]([^>]*)*?)>/i';
18
	$repl = '<img$2src="$3" srcset="${3}, ${3}&amp;dpr=2 2x, ${3}&amp;dpr=3 3x,"$4>';
19
	$content = preg_replace( $pattern, $repl, $content );
20
21
	return preg_replace( $pattern, $repl, $content );
22
}
23
24
/**
25
 * Extract all img tags from a given $content block into an array.
26
 *
27
 * @return array An array of matching arrays with two keys: 'url' and 'params'
28
 */
29
function imgix_extract_imgs( $content ) {
30
	preg_match_all( '/src=["\']http.+\/([^\s]+?)["\']/', $content, $matches );
31
	$results = array();
32
33
	if ( $matches ) {
34
		foreach ( $matches[1] as $url ) {
35
			array_push( $results, $url );
36
		}
37
	}
38
39
	return $results;
40
}
41
42
/**
43
 * Searches "$content" for all occurences of "$url" and add the given
44
 * querystring parameters to the URL, preserving existing querystring
45
 * parameters.
46
 *
47
 * @return string Content with matching URLs having the new querystrings.
48
 */
49
function imgix_apply_parameters_to_url( $url, $params, $content ) {
50
	$parts = explode( '?', $url . '?' );
51
52
	list( $base_url, $base_params ) = array( $parts[0], $parts[1] );
53
54
	$new_url = $old_url = $base_url;
55
	$new_url .= '?' . $params;
56
	$new_url .= $base_params ? '&amp;' . $base_params : '';
57
	$old_url .= $base_params ? '?' . $base_params : '';
58
59
	return str_replace( $old_url, $new_url, $content );
60
}
61
62
/**
63
 * Returns a string of global parameters to be applied in all images,
64
 * acording to plugin's settings.
65
 *
66
 * @return string Global parameters to be appened at the end of each img URL.
67
 */
68
function imgix_get_global_params_string() {
69
	global $imgix_options;
70
	$params = array();
71
	// For now, only "auto" is supported.
72
	$auto = array();
73
74
	if ( isset( $imgix_options['auto_format'] ) && $imgix_options['auto_format'] ) {
75
		array_push( $auto, 'format' );
76
	}
77
78
	if ( isset( $imgix_options['auto_enhance'] ) && $imgix_options['auto_enhance'] ) {
79
		array_push( $auto, 'enhance' );
80
	}
81
82
	if ( ! empty( $auto ) ) {
83
		array_push( $params, 'auto=' . implode( '%2C', $auto ) );
84
	}
85
86
	return implode( '&amp;', $params );
87
}
88
89
/**
90
 * Sanitize a given URL to make sure it has a scheme (or alternatively, '//' ),
91
 * host, path, and ends with a '/'.
92
 *
93
 * @return string A sanitized URL.
94
 */
95
function imgix_ensure_valid_url( $url ) {
96
	$slash = strpos( $url, '//' ) == 0 ? '//' : '';
97
98
	if ( $slash ) {
99
		$url = substr( $url, 2 );
100
	}
101
102
	$urlp = wp_parse_url( $url );
103
	$pref = array_key_exists( 'scheme', $urlp ) ? $urlp['scheme'] . '://' : $slash;
104
105
	if ( ! $slash && strpos( $pref, 'http' ) !== 0 ) {
106
		$pref = 'http://';
107
	}
108
109
	$result = $urlp['host'] ? $pref . $urlp['host'] : false;
110
111
	if ( $result ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
112
		return trailingslashit( $result );
113
	}
114
115
	return null;
116
}
117
118
/**
119
 * Parse through img element src attributes to extract height and width parameters
120
 * based on the structure of the src URL's path.
121
 *
122
 * For example, if we are giving an img src ending in "-400x300.png",
123
 * we return an array structured like so:
124
 *
125
 *    array(array(
126
 *      'raw' => '-400x300.png',
127
 *      'w' => '400',
128
 *      'h' => '300',
129
 *      'type' => 'png',
130
 *      'extra' => ''
131
 *    ))
132
 *
133
 * @return array An array of arrays that has extracted the URL's inferred w',
134
 * 'h', and 'type'
135
 */
136
function imgix_extract_img_details( $content ) {
137
	preg_match_all( '/-([0-9]+)x([0-9]+)\.([^"\']+)/', $content, $matches );
138
139
	$lookup = array( 'raw', 'w', 'h', 'type' );
140
	$data = array();
141
142
	foreach ( $matches as $k => $v ) {
0 ignored issues
show
Bug introduced by
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
143
144
		foreach ( $v as $index => $value ) {
145
146
			if ( ! array_key_exists( $index, $data ) ) {
147
				$data[ $index ] = array();
148
			}
149
150
			$key = $lookup[ $k ];
151
152
			if ( $key === 'type' ) {
153
				if ( strpos( $value, '?' ) !== false ) {
154
					$parts = explode( '?', $value );
155
					$data[ $index ]['type'] = $parts[0];
156
					$data[ $index ]['extra'] = $parts[1];
157
				} else {
158
					$data[ $index ]['type'] = $value;
159
					$data[ $index ]['extra'] = '';
160
				}
161
			} else {
162
				$data[ $index ][ $key ] = $value;
163
			}
164
		}
165
	}
166
167
	return $data;
168
}
169
170
/**
171
 * Finds references to the wordpress site URL in the given string,
172
 * (optionally prefixed by "src"), and changes them to the imgix URL.
173
 *
174
 * @return array An array countaining the final string, and a boolean value
175
 * indicating if it's different from the given input string.
176
 */
177
function imgix_replace_host( $str, $require_prefix = false ) {
178
	global $imgix_options;
179
180
	if ( ! isset( $imgix_options['cdn_link'] ) || ! $imgix_options['cdn_link'] ) {
181
		return array( $str, false );
182
	}
183
184
	$new_host = imgix_ensure_valid_url( $imgix_options['cdn_link'] );
185
	if ( ! $new_host ) {
186
		return array( $str, false );
187
	}
188
189
	// As soon as srcset is supported…
190
	// $prefix = $require_prefix? 'srcs?e?t?=[\'"]|,[\S+\n\r\s]*': '';
191
	$prefix = $require_prefix? 'src=[\'"]': '';
192
	$src = '(' . preg_quote( home_url( '/' ), '/' ) . '|\/\/)';
193
	$patt = '/(' . $prefix . ' )' . $src . '/i';
194
	$str = preg_replace( $patt, '$1' . $new_host, $str, -1, $count );
195
196
	return array( $str, (boolean) $count );
197
}
198
199
function imgix_file_url( $url ) {
200
201
	global $imgix_options;
202
203
	$imgix_url = $imgix_options['cdn_link'];
204
	$file = pathinfo( $url );
205
206
	if ( ! $imgix_url ) {
207
		return $url;
208
	}
209
210
	if ( in_array( $file['extension'], array( 'jpg', 'gif', 'png', 'jpeg' ) ) ) {
211
		return str_replace( get_bloginfo( 'wpurl' ), $imgix_url, $url );
212
	}
213
214
	return $url;
215
}
216
add_filter( 'wp_get_attachment_url', 'imgix_file_url' );
217
add_filter( 'imgix/add-image-url', 'imgix_file_url' );
218
219
/**
220
 *
221
 * @param array         $sources
222
 * @param array         $size_array
223
 * @param string        $image_src
224
 * @param array         $image_meta
225
 * @param $attachment_id
226
 *
227
 * @return array $sources
228
 */
229
function imgix_cdn_srcset( $sources, $size_array, $image_src, $image_meta, $attachment_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $size_array 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...
Unused Code introduced by
The parameter $image_src 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...
Unused Code introduced by
The parameter $image_meta 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...
Unused Code introduced by
The parameter $attachment_id 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...
230
231
	global $imgix_options;
232
233
	$imgix_url = $imgix_options['cdn_link'];
0 ignored issues
show
Unused Code introduced by
$imgix_url is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
234
235
	foreach ( $sources as $source ) {
236
237
		$sources[ $source['value'] ]['url'] = apply_filters( 'imgix/add-image-url', $sources[ $source['value'] ]['url'] );
238
239
	}
240
241
	return $sources;
242
}
243
add_filter( 'wp_calculate_image_srcset', 'imgix_cdn_srcset', 10, 5 );
244
245
function imgix_replace_non_wp_images( $content ) {
246
	list( $content, $match ) = imgix_replace_host( $content, true );
247
248
	if ( $match ) {
249
		// Apply image-tag-encoded params for every image in $content.
250
		foreach ( imgix_extract_img_details( $content ) as $img ) {
251
			$to_replace = $img['raw'];
252
			$extra_params = $img['extra'] ? '&amp;' . $img['extra'] : '';
253
			$new_url = '.' . $img['type'] . '?h=' . $img['h'] . '&amp;w=' . $img['w'] . $extra_params;
254
			$content = str_replace( $to_replace, $new_url, $content );
255
		}
256
257
		// Apply global parameters.
258
		$g_params = imgix_get_global_params_string();
259
		foreach ( imgix_extract_imgs( $content ) as $img_url ) {
260
			$content = imgix_apply_parameters_to_url( $img_url, $g_params, $content );
261
		}
262
	}
263
	return $content;
264
}
265
266
add_filter( 'the_content', 'imgix_replace_non_wp_images' );
267
268
function imgix_wp_head() {
269
	global $imgix_options;
270
271
	if ( isset( $imgix_options['cdn_link'] ) && $imgix_options['cdn_link'] ) {
272
		printf( "<link rel='dns-prefetch' href='%s'/>",
273
			esc_url( preg_replace( '/^https?:/', '', untrailingslashit( $imgix_options['cdn_link'] ) ) )
274
		);
275
	}
276
}
277
278
add_action( 'wp_head', 'imgix_wp_head', 1 );
279
280
if ( isset( $imgix_options['add_dpi2_srcset'] ) && $imgix_options['add_dpi2_srcset'] ) {
281
	function imgix_buffer_start() {
282
		ob_start( 'imgix_add_retina' );
283
	}
284
285
	function imgix_buffer_end() {
286
		ob_end_flush();
287
	}
288
289
	add_action( 'after_setup_theme', 'imgix_buffer_start' );
290
	add_action( 'shutdown', 'imgix_buffer_end' );
291
	add_filter( 'the_content', 'imgix_add_retina' );
292
}
293