| @@ -90,14 +90,14 @@ | ||
| 90 | 90 | } |
| 91 | 91 | |
| 92 | 92 | $images = []; |
| 93 | 93 | |
| 94 | - foreach (self::candidates($post_id) as $url) { | |
| 94 | + foreach (self::candidates($post_id) as $candidate) { | |
| 95 | 95 | if (count($images) >= $limit - 1) { |
| 96 | 96 | break; |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | - $url = self::usable($url); | |
| 99 | + $url = self::usable($candidate['url']); | |
| 100 | 100 | |
| 101 | 101 | if ('' === $url) { |
| 102 | 102 | continue; |
| 103 | 103 | } |
| @@ -108,9 +108,9 @@ | ||
| 108 | 108 | continue; |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | 111 | $seen[$print] = true; |
| 112 | - $images[] = self::describe($url, $post_id); | |
| 112 | + $images[] = self::describe($url, $post_id, $candidate['attachment_id']); | |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | 115 | /** |
| 116 | 116 | * Filter the secondary Open Graph images for a page. |
| @@ -123,16 +123,20 @@ | ||
| 123 | 123 | return (array) apply_filters('thinkrank_og_extra_images', $images, $post_id); |
| 124 | 124 | } |
| 125 | 125 | |
| 126 | 126 | /** |
| 127 | - * Candidate URLs, in the order they should be offered. | |
| 127 | + * Candidate images, in the order they should be offered. | |
| 128 | 128 | * |
| 129 | + * Each carries the attachment ID it is believed to come from, where there | |
| 130 | + * is one to be had without asking the database: the featured image's own, | |
| 131 | + * or the `wp-image-{ID}` the editor wrote on a body image (#847). | |
| 132 | + * | |
| 129 | 133 | * @since 2.7.0 |
| 130 | 134 | * @param int $post_id Post being rendered. |
| 131 | - * @return array<int, string> | |
| 135 | + * @return array<int, array{url:string, attachment_id:int}> | |
| 132 | 136 | */ |
| 133 | 137 | private static function candidates(int $post_id): array { |
| 134 | - $urls = []; | |
| 138 | + $candidates = []; | |
| 135 | 139 | |
| 136 | 140 | // The featured image. Usually this IS the primary and the fingerprint |
| 137 | 141 | // check drops it; it matters when a per-post social override displaced |
| 138 | 142 | // it, which is exactly the case where a second image is worth offering. |
| @@ -141,21 +145,24 @@ | ||
| 141 | 145 | if ($thumbnail_id) { |
| 142 | 146 | $src = wp_get_attachment_image_src($thumbnail_id, 'large'); |
| 143 | 147 | |
| 144 | 148 | if (is_array($src) && !empty($src[0])) { |
| 145 | - $urls[] = (string) $src[0]; | |
| 149 | + $candidates[] = [ | |
| 150 | + 'url' => (string) $src[0], | |
| 151 | + 'attachment_id' => $thumbnail_id, | |
| 152 | + ]; | |
| 146 | 153 | } |
| 147 | 154 | } |
| 148 | 155 | |
| 149 | - foreach (self::from_content($post_id) as $url) { | |
| 150 | - $urls[] = $url; | |
| 156 | + foreach (self::from_content($post_id) as $candidate) { | |
| 157 | + $candidates[] = $candidate; | |
| 151 | 158 | } |
| 152 | 159 | |
| 153 | - return $urls; | |
| 160 | + return $candidates; | |
| 154 | 161 | } |
| 155 | 162 | |
| 156 | 163 | /** |
| 157 | - * Image URLs in the post body, in document order. | |
| 164 | + * Images in the post body, in document order. | |
| 158 | 165 | * |
| 159 | 166 | * Reads the stored content rather than running it through `the_content`. |
| 160 | 167 | * This is called while the document head is being written, and rendering |
| 161 | 168 | * the body there would run every shortcode and block on the page for the |
| @@ -162,9 +169,9 @@ | ||
| 162 | 169 | * sake of a few `src` attributes. |
| 163 | 170 | * |
| 164 | 171 | * @since 2.7.0 |
| 165 | 172 | * @param int $post_id Post being rendered. |
| 166 | - * @return array<int, string> | |
| 173 | + * @return array<int, array{url:string, attachment_id:int}> | |
| 167 | 174 | */ |
| 168 | 175 | private static function from_content(int $post_id): array { |
| 169 | 176 | $content = (string) get_post_field('post_content', $post_id); |
| 170 | 177 | |
| @@ -175,17 +182,20 @@ | ||
| 175 | 182 | if (!preg_match_all('/<img\b[^>]*>/i', $content, $tags)) { |
| 176 | 183 | return []; |
| 177 | 184 | } |
| 178 | 185 | |
| 179 | - $urls = []; | |
| 186 | + $images = []; | |
| 180 | 187 | |
| 181 | 188 | foreach ($tags[0] as $tag) { |
| 182 | 189 | if (preg_match('/\bsrc\s*=\s*["\']([^"\']+)["\']/i', $tag, $match)) { |
| 183 | - $urls[] = $match[1]; | |
| 190 | + $images[] = [ | |
| 191 | + 'url' => $match[1], | |
| 192 | + 'attachment_id' => Attachment_Lookup::hint_from_markup($tag), | |
| 193 | + ]; | |
| 184 | 194 | } |
| 185 | 195 | } |
| 186 | 196 | |
| 187 | - return $urls; | |
| 197 | + return $images; | |
| 188 | 198 | } |
| 189 | 199 | |
| 190 | 200 | /** |
| 191 | 201 | * A URL Open Graph can actually use, or '' to skip it. |
| @@ -221,34 +231,33 @@ | ||
| 221 | 231 | * to lay out a card before it has fetched the file. |
| 222 | 232 | * |
| 223 | 233 | * @since 2.7.0 |
| 224 | 234 | * |
| 225 | - * @param string $url Image URL. | |
| 226 | - * @param int $post_id Post being rendered, for the alt fallback. | |
| 235 | + * @param string $url Image URL. | |
| 236 | + * @param int $post_id Post being rendered, for the alt fallback. | |
| 237 | + * @param int $attachment_id Attachment the URL is believed to come from, | |
| 238 | + * or 0. Checked before it is trusted. | |
| 227 | 239 | * @return array<string, mixed> |
| 228 | 240 | */ |
| 229 | - private static function describe(string $url, int $post_id): array { | |
| 241 | + private static function describe(string $url, int $post_id, int $attachment_id = 0): array { | |
| 230 | 242 | $image = ['url' => $url]; |
| 231 | 243 | |
| 232 | - $attachment_id = (int) attachment_url_to_postid($url); | |
| 244 | + $attachment_id = Attachment_Lookup::id_from_url($url, $attachment_id); | |
| 233 | 245 | |
| 234 | 246 | if ($attachment_id) { |
| 235 | - $meta = wp_get_attachment_metadata($attachment_id); | |
| 247 | + // The file this URL names, which for a body image is usually a | |
| 248 | + // generated size rather than the original upload. | |
| 249 | + $file = Attachment_Lookup::describe($attachment_id, $url); | |
| 236 | 250 | |
| 237 | 251 | // Vector uploads report 0x0; publishing that as a dimension is |
| 238 | 252 | // invalid, so the companions are omitted rather than zeroed. |
| 239 | - $width = is_array($meta) && isset($meta['width']) ? (int) $meta['width'] : 0; | |
| 240 | - $height = is_array($meta) && isset($meta['height']) ? (int) $meta['height'] : 0; | |
| 241 | - | |
| 242 | - if ($width > 0 && $height > 0) { | |
| 243 | - $image['width'] = $width; | |
| 244 | - $image['height'] = $height; | |
| 253 | + if ($file['width'] > 0 && $file['height'] > 0) { | |
| 254 | + $image['width'] = $file['width']; | |
| 255 | + $image['height'] = $file['height']; | |
| 245 | 256 | } |
| 246 | 257 | |
| 247 | - $type = (string) get_post_mime_type($attachment_id); | |
| 248 | - | |
| 249 | - if ('' !== $type) { | |
| 250 | - $image['type'] = $type; | |
| 258 | + if ('' !== $file['type']) { | |
| 259 | + $image['type'] = $file['type']; | |
| 251 | 260 | } |
| 252 | 261 | |
| 253 | 262 | $alt = trim((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true)); |
| 254 | 263 | |