1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
|
<?php /** * WPSEO plugin file. * * @package WPSEO */
/** * WPSEO_Image_Utils. */ class WPSEO_Image_Utils {
/** * Find an attachment ID for a given URL. * * @param string $url The URL to find the attachment for. * * @return int The found attachment ID, or 0 if none was found. */ public static function get_attachment_by_url( $url ) { /* * As get_attachment_by_url won't work on resized versions of images, * we strip out the size part of an image URL. */ $url = preg_replace( '/(.*)-\d+x\d+\.(jpg|png|gif)$/', '$1.$2', $url );
// Don't try to do this for external URLs. if ( strpos( $url, get_site_url() ) !== 0 ) { return 0; }
if ( function_exists( 'wpcom_vip_attachment_url_to_postid' ) ) { // @codeCoverageIgnoreStart -- We can't test this properly. return (int) wpcom_vip_attachment_url_to_postid( $url ); // @codeCoverageIgnoreEnd -- The rest we _can_ test. }
return self::attachment_url_to_postid( $url ); }
/** * Implements the attachment_url_to_postid with use of WP Cache. * * @param string $url The attachment URL for which we want to know the Post ID. * * @return int The Post ID belonging to the attachment, 0 if not found. */ protected static function attachment_url_to_postid( $url ) { $cache_key = sprintf( 'yoast_attachment_url_post_id_%s', md5( $url ) );
// Set the ID based on the hashed URL in the cache. $id = wp_cache_get( $cache_key );
if ( $id === 'not_found' ) { return 0; }
// ID is found in cache, return. if ( $id !== false ) { return $id; }
// phpcs:ignore WordPress.VIP.RestrictedFunctions -- We use the WP COM version if we can, see above. $id = attachment_url_to_postid( $url );
if ( empty( $id ) ) { wp_cache_set( $cache_key, 'not_found', '', ( 12 * HOUR_IN_SECONDS + wp_rand( 0, ( 4 * HOUR_IN_SECONDS ) ) ) ); return 0; }
// We have the Post ID, but it's not in the cache yet. We do that here and return. wp_cache_set( $cache_key, $id, '', ( 24 * HOUR_IN_SECONDS + wp_rand( 0, ( 12 * HOUR_IN_SECONDS ) ) ) ); return $id; }
/** * Retrieves the image data. * * @param array $image Image array with URL and metadata. * @param int $attachment_id Attachment ID. * * @return false|array $image { * Array of image data * * @type string $alt Image's alt text. * @type string $alt Image's alt text. * @type int $width Width of image. * @type int $height Height of image. * @type string $type Image's MIME type. * @type string $url Image's URL. * @type int $filesize The file size in bytes, if already set. * } */ public static function get_data( $image, $attachment_id ) { if ( ! is_array( $image ) ) { return false; }
// Deals with non-set keys and values being null or false. if ( empty( $image['width'] ) || empty( $image['height'] ) ) { return false; }
$image['id'] = $attachment_id; $image['alt'] = self::get_alt_tag( $attachment_id ); $image['pixels'] = ( (int) $image['width'] * (int) $image['height'] );
if ( ! isset( $image['type'] ) ) { $image['type'] = get_post_mime_type( $attachment_id ); }
// Keep only the keys we need, and nothing else. return array_intersect_key( $image, array_flip( array( 'id', 'alt', 'path', 'width', 'height', 'pixels', 'type', 'size', 'url', 'filesize' ) ) ); }
/** * Checks a size version of an image to see if it's not too heavy. * * @param array $image Image to check the file size of. * * @return bool True when the image is within limits, false if not. */ public static function has_usable_file_size( $image ) { if ( ! is_array( $image ) || $image === array() ) { return false; }
/** * Filter: 'wpseo_image_image_weight_limit' - Determines what the maximum weight (in bytes) of an image is allowed to be, default is 2 MB. * * @api int - The maximum weight (in bytes) of an image. */ $max_size = apply_filters( 'wpseo_image_image_weight_limit', 2097152 );
// We cannot check without a path, so assume it's fine. if ( ! isset( $image['path'] ) ) { return true; }
return ( self::get_file_size( $image ) <= $max_size ); }
/** * Find the right version of an image based on size. * * @param int $attachment_id Attachment ID. * @param string $size Size name. * * @return array|false Returns an array with image data on success, false on failure. */ public static function get_image( $attachment_id, $size ) { $image = false; if ( $size === 'full' ) { $image = self::get_full_size_image_data( $attachment_id ); }
if ( ! $image ) { $image = image_get_intermediate_size( $attachment_id, $size ); $image['size'] = $size; }
if ( ! $image ) { return false; }
return self::get_data( $image, $attachment_id ); }
/** * Returns the image data for the full size image. * * @param int $attachment_id Attachment ID. * * @return array|false Array when there is a full size image. False if not. */ protected static function get_full_size_image_data( $attachment_id ) { $image = wp_get_attachment_metadata( $attachment_id ); if ( ! is_array( $image ) ) { return false; }
$image['url'] = wp_get_attachment_image_url( $attachment_id, 'full' ); $image['path'] = get_attached_file( $attachment_id ); $image['size'] = 'full';
return $image; }
/** * Finds the full file path for a given image file. * * @param string $path The relative file path. * * @return string The full file path. */ public static function get_absolute_path( $path ) { static $uploads;
if ( $uploads === null ) { $uploads = wp_get_upload_dir(); }
// Add the uploads basedir if the path does not start with it. if ( empty( $uploads['error'] ) && strpos( $path, $uploads['basedir'] . DIRECTORY_SEPARATOR ) !== 0 ) { return $uploads['basedir'] . DIRECTORY_SEPARATOR . ltrim( $path, DIRECTORY_SEPARATOR ); }
return $path; }
/** * Get the relative path of the image. * * @param string $img Image URL. * * @return string The expanded image URL. */ public static function get_relative_path( $img ) { if ( $img[0] !== '/' ) { return $img; }
// If it's a relative URL, it's relative to the domain, not necessarily to the WordPress install, we // want to preserve domain name and URL scheme (http / https) though. $parsed_url = wp_parse_url( home_url() ); $img = $parsed_url['scheme'] . '://' . $parsed_url['host'] . $img;
return $img; }
/** * Get the image file size. * * @param array $image An image array object. * * @return int The file size in bytes. */ public static function get_file_size( $image ) { if ( isset( $image['filesize'] ) ) { return $image['filesize']; }
// If the file size for the file is over our limit, we're going to go for a smaller version. // @todo Save the filesize to the image metadata. // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- If file size doesn't properly return, we'll not fail. return @filesize( self::get_absolute_path( $image['path'] ) ); }
/** * Returns the different image variations for consideration. * * @param int $attachment_id The attachment to return the variations for. * * @return array The different variations possible for this attachment ID. */ public static function get_variations( $attachment_id ) { $variations = array();
foreach ( self::get_sizes() as $size ) { $variation = self::get_image( $attachment_id, $size );
// The get_image function returns false if the size doesn't exist for this attachment. if ( $variation ) { $variations[] = $variation; } }
return $variations; }
/** * Check original size of image. If original image is too small, return false, else return true. * * Filters a list of variations by a certain set of usable dimensions. * * @param array $usable_dimensions { * The parameters to check against. * * @type int $min_width Minimum width of image. * @type int $max_width Maximum width of image. * @type int $min_height Minimum height of image. * @type int $max_height Maximum height of image. * } * @param array $variations The variations that should be considered. * * @return array Whether a variation is fit for display or not. */ public static function filter_usable_dimensions( $usable_dimensions, $variations ) { $filtered = array();
foreach ( $variations as $variation ) { $dimensions = $variation;
if ( self::has_usable_dimensions( $dimensions, $usable_dimensions ) ) { $filtered[] = $variation; } }
return $filtered; }
/** * Filters a list of variations by (disk) file size. * * @param array $variations The variations to consider. * * @return array The validations that pass the required file size limits. */ public static function filter_usable_file_size( $variations ) { foreach ( $variations as $variation ) { // We return early to prevent measuring the file size of all the variations. if ( self::has_usable_file_size( $variation ) ) { return array( $variation ); } }
return array(); }
/** * Retrieve the internal WP image file sizes. * * @return array $image_sizes An array of image sizes. */ public static function get_sizes() { /** * Filter: 'wpseo_image_sizes' - Determines which image sizes we'll loop through to get an appropriate image. * * @api array - The array of image sizes to loop through. */ return apply_filters( 'wpseo_image_sizes', array( 'full', 'large', 'medium_large' ) ); }
/** * Grabs an image alt text. * * @param int $attachment_id The attachment ID. * * @return string The image alt text. */ public static function get_alt_tag( $attachment_id ) { return (string) get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ); }
/** * Checks whether an img sizes up to the parameters. * * @param array $dimensions The image values. * @param array $usable_dimensions The parameters to check against. * * @return bool True if the image has usable measurements, false if not. */ private static function has_usable_dimensions( $dimensions, $usable_dimensions ) { foreach ( array( 'width', 'height' ) as $param ) { $minimum = $usable_dimensions[ 'min_' . $param ]; $maximum = $usable_dimensions[ 'max_' . $param ];
$current = $dimensions[ $param ]; if ( ( $current < $minimum ) || ( $current > $maximum ) ) { return false; } }
return true; }
/** * Gets the post's first usable content image. Null if none is available. * * @param int $post_id The post id. * * @return string|null The image URL. */ public static function get_first_usable_content_image_for_post( $post_id = null ) { $post = get_post( $post_id );
if ( $post === null ) { return null; }
$image_finder = new WPSEO_Content_Images(); $images = $image_finder->get_images( $post->ID, $post );
if ( ! is_array( $images ) || empty( $images ) ) { return null; }
$image_url = reset( $images ); if ( ! $image_url ) { return null; }
return $image_url; }
/** * Retrieves an attachment ID for an image uploaded in the settings. * * Due to self::get_attachment_by_url returning 0 instead of false. * 0 is also a possibility when no ID is available. * * @param string $setting The setting the image is stored in. * * @return int|bool The attachment id, or false or 0 if no ID is available. */ public static function get_attachment_id_from_settings( $setting ) { $image_id = WPSEO_Options::get( $setting . '_id', false ); if ( ! $image_id ) { $image = WPSEO_Options::get( $setting, false ); if ( $image ) { // There is not an option to put a URL in an image field in the settings anymore, only to upload it through the media manager. // This means an attachment always exists, so doing this is only needed once. $image_id = self::get_attachment_by_url( $image ); WPSEO_Options::set( $setting . '_id', $image_id ); } }
return $image_id; } }
|