PluginProbe
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution / trunk
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution vtrunk
2.0.1 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.9.0 2.0.0
solid-performance / src / Performance / Image_Transformation / Image.php

Image.php in Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution trunk, at src/Performance/Image_Transformation/Image.php

173 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The Image Class.
4 *
5 * @package SolidWP\Performance
6 */
7
8 declare( strict_types=1 );
9
10 namespace SolidWP\Performance\Image_Transformation;
11
12 /**
13 * Helps with detecting image data where we may not have standard "WordPress" ways
14 * to get this data.
15 *
16 * @package SolidWP\Performance
17 */
18 final class Image {
19
20 /**
21 * Memoization cache of IDs, indexed by their URL.
22 *
23 * @var array<string, int>
24 */
25 private array $ids = [];
26
27 /**
28 * Memoization cache of image metadata, indexed by their ID.
29 *
30 * @var array<int, mixed[]>
31 */
32 private array $meta = [];
33
34 /**
35 * Last resort to detect an image's size based on its URL.
36 *
37 * @param string $url The original image URL.
38 * @param int $id An optional attachment ID.
39 *
40 * @return array{0: int, 1: int}|string An array with width/height values or the thumbnail size name.
41 */
42 public function detect_size( string $url, int $id = 0 ) {
43 $id = $id ?: $this->url_to_id( $url );
44
45 if ( ! $id ) {
46 return '';
47 }
48
49 $path = wp_parse_url( $url, PHP_URL_PATH );
50
51 if ( ! $path ) {
52 return '';
53 }
54
55 $file = basename( $path );
56
57 if ( ! $file ) {
58 return '';
59 }
60
61 // Try to prevent a query.
62 $this->meta[ $id ] ??= wp_get_attachment_metadata( $id );
63
64 $meta = $this->meta[ $id ];
65 $yearmonth = ltrim( wp_get_upload_dir()['subdir'], '/' );
66 $full = $meta['file'] ?? '';
67 $original = $meta['original_image'] ?? '';
68
69 /*
70 * Check if this is the full-sized or original image.
71 *
72 * e.g.
73 * my-image.jpg
74 * my-image-scaled.jpg
75 * 2025/02/my-image.jpg
76 * 2025/02/my-image-scaled.jpg
77 * my-image-original-upload-name-if-scaled.jpg
78 */
79 if (
80 $file === $full ||
81 "$yearmonth/$file" === $full ||
82 $file === $original
83 ) {
84 return [
85 (int) ( $meta['width'] ?? 0 ),
86 (int) ( $meta['height'] ?? 0 ),
87 ];
88 }
89
90 if ( empty( $meta['sizes'] ) ) {
91 return '';
92 }
93
94 foreach ( $meta['sizes'] as $name => $sizes ) {
95 if ( empty( $sizes['file'] ) || $file !== $sizes['file'] ) {
96 continue;
97 }
98
99 // Try to return the width/height first to prevent more lookups.
100 if ( isset( $sizes['width'], $sizes['height'] ) ) {
101 return [
102 (int) $sizes['width'],
103 (int) $sizes['height'],
104 ];
105 }
106
107 // Otherwise, return the thumbnail size name.
108 return $name;
109 }
110
111 return '';
112 }
113
114 /**
115 * Support getting an ID for image URLs with their thumbnail dimensions in the file name with
116 * a fallback to also check for the -scaled version.
117 *
118 * @param string $url The original attachment URL.
119 *
120 * @return int
121 */
122 public function url_to_id( string $url ): int {
123 if ( isset( $this->ids[ $url ] ) ) {
124 return $this->ids[ $url ];
125 }
126
127 $full_image_url = $this->build_url( $url );
128
129 if ( ! $full_image_url ) {
130 return 0;
131 }
132
133 $id = attachment_url_to_postid( $full_image_url );
134
135 // Fallback to check for a -scaled version of the full image.
136 if ( ! $id ) {
137 $id = attachment_url_to_postid( $this->build_url( $url, '-scaled' ) );
138 }
139
140 // Cache result.
141 $this->ids[ $url ] = $id;
142
143 return $id;
144 }
145
146 /**
147 * Remove the thumbnail dimensions portion of a URL to find the main URL that would be stored in
148 * the database.
149 *
150 * @param string $url The original URL.
151 * @param string $replacement The string to use when replacing the widthxheight portion of the URL.
152 *
153 * @return string
154 */
155 private function build_url( string $url, string $replacement = '' ): string {
156 $parsed_url = wp_parse_url( $url );
157
158 if ( ! isset( $parsed_url['path'] ) ) {
159 return '';
160 }
161
162 $path = $parsed_url['path'];
163
164 // Replace the last occurrence of -WIDTHxHEIGHT before the file extension.
165 $path = preg_replace( '/-\d+x\d+(?=\.[a-zA-Z0-9]+$)/', $replacement, $path, 1 );
166
167 // Reconstruct the image URL.
168 return isset( $parsed_url['scheme'], $parsed_url['host'] )
169 ? "{$parsed_url['scheme']}://{$parsed_url['host']}$path"
170 : $path;
171 }
172 }
173