| @@ -51,33 +51,8 @@ | ||
| 51 | 51 | */ |
| 52 | 52 | private ?string $separator = null; |
| 53 | 53 | |
| 54 | 54 | /** |
| 55 | - * Attachment IDs whose cache purge is being held back, or null when not. | |
| 56 | - * | |
| 57 | - * A bulk fill writes alt text onto hundreds of attachments that between | |
| 58 | - * them appear on a handful of pages. Resolving usage per attachment would | |
| 59 | - * run one unindexed postmeta scan per image; holding the IDs and resolving | |
| 60 | - * the whole batch in one query at the end is the same answer for a | |
| 61 | - * fraction of the work. | |
| 62 | - * | |
| 63 | - * @since 2.12.0 | |
| 64 | - * @var int[]|null | |
| 65 | - */ | |
| 66 | - private ?array $deferred_alt_purge = null; | |
| 67 | - | |
| 68 | - /** | |
| 69 | - * What the most recent purge_alt_caches() call did. | |
| 70 | - * | |
| 71 | - * Read by the MCP abilities, which have to report the cache outcome | |
| 72 | - * alongside the write and cannot see inside fill_attachment_alt(). | |
| 73 | - * | |
| 74 | - * @since 2.12.0 | |
| 75 | - * @var array{posts: int[], warnings: string[]} | |
| 76 | - */ | |
| 77 | - private array $last_alt_cache_purge = ['posts' => [], 'warnings' => []]; | |
| 78 | - | |
| 79 | - /** | |
| 80 | 55 | * Constructor |
| 81 | 56 | * |
| 82 | 57 | * @since 1.0.0 |
| 83 | 58 | */ |
| @@ -294,11 +269,9 @@ | ||
| 294 | 269 | // filename-based formats and for images that need no injection. |
| 295 | 270 | $needs_attachment = |
| 296 | 271 | ($alt_missing && $this->format_uses_attachment($settings['alt_format'] ?? '')) || |
| 297 | 272 | ($title_missing && $this->format_uses_attachment($settings['title_format'] ?? '')); |
| 298 | - $attachment_id = $needs_attachment | |
| 299 | - ? $this->url_to_attachment_id($src, (string) ($attributes['class'] ?? '')) | |
| 300 | - : 0; | |
| 273 | + $attachment_id = $needs_attachment ? $this->url_to_attachment_id($src) : 0; | |
| 301 | 274 | |
| 302 | 275 | // Handle ALT attribute |
| 303 | 276 | if ($alt_missing) { |
| 304 | 277 | $alt_val = $this->generate_attribute_value($settings['alt_format'] ?? '', $attachment_id, $id_to_pass, $count, $src); |
| @@ -405,23 +378,28 @@ | ||
| 405 | 378 | || strpos($format, '%image_caption%') !== false; |
| 406 | 379 | } |
| 407 | 380 | |
| 408 | 381 | /** |
| 409 | - * Resolve an attachment ID from a source URL. | |
| 382 | + * Resolve an attachment ID from a source URL, memoized per request. | |
| 410 | 383 | * |
| 411 | - * Body images are almost always inserted at a generated size, which | |
| 412 | - * `attachment_url_to_postid()` cannot match, so %image_title% and | |
| 413 | - * %image_caption% resolved to nothing for them. Attachment_Lookup reads the | |
| 414 | - * `wp-image-{ID}` class the editor wrote first, and caches whatever still | |
| 415 | - * has to be asked of the database (#847). | |
| 384 | + * `attachment_url_to_postid()` issues its own DB query, so repeated identical | |
| 385 | + * URLs on a page (galleries, duplicated images) are cached here. | |
| 416 | 386 | * |
| 417 | 387 | * @since 1.19.1 |
| 418 | - * @param string $src Source URL. | |
| 419 | - * @param string $classes The image's class attribute, for its wp-image-{ID}. | |
| 388 | + * @param string $src Source URL. | |
| 420 | 389 | * @return int Attachment ID, or 0 if not a media-library image. |
| 421 | 390 | */ |
| 422 | - private function url_to_attachment_id(string $src, string $classes = ''): int { | |
| 423 | - return Attachment_Lookup::id_from_url($src, Attachment_Lookup::hint_from_markup($classes)); | |
| 391 | + private function url_to_attachment_id(string $src): int { | |
| 392 | + if ($src === '') { | |
| 393 | + return 0; | |
| 394 | + } | |
| 395 | + | |
| 396 | + static $cache = []; | |
| 397 | + if (!array_key_exists($src, $cache)) { | |
| 398 | + $cache[$src] = attachment_url_to_postid($src); | |
| 399 | + } | |
| 400 | + | |
| 401 | + return $cache[$src]; | |
| 424 | 402 | } |
| 425 | 403 | |
| 426 | 404 | /** |
| 427 | 405 | * Inject (or replace an empty) alt/title attribute on a single <img> tag. |
| @@ -552,13 +530,11 @@ | ||
| 552 | 530 | * |
| 553 | 531 | * @since 1.19.1 |
| 554 | 532 | * @param int $attachment_id The attachment ID. |
| 555 | 533 | * @param bool $overwrite When false, images that already have alt text are left untouched. |
| 556 | - * @param bool $purge Clear the caches that already rendered this image. Pass false only | |
| 557 | - * when the attachment cannot yet appear on any page. | |
| 558 | 534 | * @return bool True when the attachment now has the generated alt text; false when skipped or on failure. |
| 559 | 535 | */ |
| 560 | - public function fill_attachment_alt(int $attachment_id, bool $overwrite = false, bool $purge = true): bool { | |
| 536 | + public function fill_attachment_alt(int $attachment_id, bool $overwrite = false): bool { | |
| 561 | 537 | if (!wp_attachment_is_image($attachment_id)) { |
| 562 | 538 | return false; |
| 563 | 539 | } |
| 564 | 540 | |
| @@ -598,106 +574,12 @@ | ||
| 598 | 574 | // Already correct — treat as success without a redundant write. |
| 599 | 575 | return true; |
| 600 | 576 | } |
| 601 | 577 | |
| 602 | - $written = update_post_meta($attachment_id, '_wp_attachment_image_alt', $value) !== false; | |
| 603 | - | |
| 604 | - if ($written) { | |
| 605 | - $this->record_alt_write($attachment_id, $purge); | |
| 606 | - } | |
| 607 | - | |
| 608 | - return $written; | |
| 578 | + return update_post_meta($attachment_id, '_wp_attachment_image_alt', $value) !== false; | |
| 609 | 579 | } |
| 610 | 580 | |
| 611 | 581 | /** |
| 612 | - * Note that an attachment's alt text changed, and clear what rendered it. | |
| 613 | - * | |
| 614 | - * @since 2.12.0 | |
| 615 | - * @param int $attachment_id Attachment whose alt text was just written. | |
| 616 | - * @param bool $purge False when nothing can be displaying this attachment yet. | |
| 617 | - * @return void | |
| 618 | - */ | |
| 619 | - protected function record_alt_write(int $attachment_id, bool $purge = true): void { | |
| 620 | - // Recorded during a bulk run whatever $purge says: the batch resolves | |
| 621 | - // the whole list at the end, and bulk_fill_missing_alt() counts its | |
| 622 | - // writes from this list. | |
| 623 | - if (null !== $this->deferred_alt_purge) { | |
| 624 | - $this->deferred_alt_purge[] = $attachment_id; | |
| 625 | - | |
| 626 | - return; | |
| 627 | - } | |
| 628 | - | |
| 629 | - // A fresh upload cannot be on any page, so there is nothing to clear | |
| 630 | - // and nothing for thinkrank_image_alt_updated to report. Listeners | |
| 631 | - // that care about new files already have core's add_attachment. | |
| 632 | - if (!$purge) { | |
| 633 | - return; | |
| 634 | - } | |
| 635 | - | |
| 636 | - $this->purge_alt_caches([$attachment_id]); | |
| 637 | - } | |
| 638 | - | |
| 639 | - /** | |
| 640 | - * Drop cached renderings of every page that shows these attachments. | |
| 641 | - * | |
| 642 | - * Alt text is written to post meta but read out of HTML that other | |
| 643 | - * software has already rendered and stored, so the write alone changes | |
| 644 | - * nothing a visitor sees. Elementor keeps rendered widgets for 24 hours by | |
| 645 | - * default and page caches keep whole documents, which is how an agent came | |
| 646 | - * to report a successful alt-text write against a page still showing the | |
| 647 | - * old words (#763). | |
| 648 | - * | |
| 649 | - * @since 2.12.0 | |
| 650 | - * @param int[] $attachment_ids Attachments whose alt text changed. | |
| 651 | - * @return array{posts: int[], warnings: string[]} Posts purged, and caches left for the user to clear. | |
| 652 | - */ | |
| 653 | - public function purge_alt_caches(array $attachment_ids): array { | |
| 654 | - $ids = array_values(array_unique(array_filter(array_map('intval', $attachment_ids)))); | |
| 655 | - | |
| 656 | - if ([] === $ids) { | |
| 657 | - $this->last_alt_cache_purge = ['posts' => [], 'warnings' => []]; | |
| 658 | - | |
| 659 | - return $this->last_alt_cache_purge; | |
| 660 | - } | |
| 661 | - | |
| 662 | - $posts = Attachment_Usage::posts_using($ids); | |
| 663 | - $purged = Cache_Purger::purge_posts($posts); | |
| 664 | - | |
| 665 | - foreach ($ids as $id) { | |
| 666 | - /** | |
| 667 | - * Fires after ThinkRank writes an image's alt text and clears the | |
| 668 | - * caches it knows about. | |
| 669 | - * | |
| 670 | - * Builders and cache layers ThinkRank does not handle can listen | |
| 671 | - * here to drop their own rendering of the affected posts. | |
| 672 | - * | |
| 673 | - * @since 2.12.0 | |
| 674 | - * | |
| 675 | - * @param int $attachment_id The attachment whose alt text changed. | |
| 676 | - * @param int[] $post_ids Posts found to display that attachment. | |
| 677 | - */ | |
| 678 | - do_action('thinkrank_image_alt_updated', $id, $posts); | |
| 679 | - } | |
| 680 | - | |
| 681 | - $this->last_alt_cache_purge = [ | |
| 682 | - 'posts' => $purged, | |
| 683 | - 'warnings' => Cache_Purger::warnings(), | |
| 684 | - ]; | |
| 685 | - | |
| 686 | - return $this->last_alt_cache_purge; | |
| 687 | - } | |
| 688 | - | |
| 689 | - /** | |
| 690 | - * The outcome of the most recent alt-text cache purge in this request. | |
| 691 | - * | |
| 692 | - * @since 2.12.0 | |
| 693 | - * @return array{posts: int[], warnings: string[]} | |
| 694 | - */ | |
| 695 | - public function last_alt_cache_purge(): array { | |
| 696 | - return $this->last_alt_cache_purge; | |
| 697 | - } | |
| 698 | - | |
| 699 | - /** | |
| 700 | 582 | * Describe an attachment with the vision model. |
| 701 | 583 | * |
| 702 | 584 | * Never throws: alt text generation runs in batches over a whole media |
| 703 | 585 | * library, and one unreadable image or a rate-limit blip must not abort |
| @@ -783,35 +665,21 @@ | ||
| 783 | 665 | // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- The pager must walk the same unfiltered set count_images() counts, or it can never reach the total (#322). |
| 784 | 666 | 'suppress_filters' => true, |
| 785 | 667 | ]); |
| 786 | 668 | |
| 669 | + $updated = 0; | |
| 670 | + $skipped = 0; | |
| 787 | 671 | $processed = 0; |
| 788 | 672 | |
| 789 | - // Hold every purge until the batch is done: see $deferred_alt_purge. | |
| 790 | - $this->deferred_alt_purge = []; | |
| 791 | - | |
| 792 | - try { | |
| 793 | - foreach ($ids as $id) { | |
| 794 | - $processed++; | |
| 795 | - $this->fill_attachment_alt((int) $id, $overwrite); | |
| 673 | + foreach ($ids as $id) { | |
| 674 | + $processed++; | |
| 675 | + if ($this->fill_attachment_alt((int) $id, $overwrite)) { | |
| 676 | + $updated++; | |
| 677 | + } else { | |
| 678 | + $skipped++; | |
| 796 | 679 | } |
| 797 | - } finally { | |
| 798 | - $written = array_values(array_unique($this->deferred_alt_purge ?? [])); | |
| 799 | - $this->deferred_alt_purge = null; | |
| 800 | - $cache = $this->purge_alt_caches($written); | |
| 801 | 680 | } |
| 802 | 681 | |
| 803 | - // Counted from the writes that actually happened, not from what | |
| 804 | - // fill_attachment_alt() returned. It answers true for an image whose | |
| 805 | - // stored alt already equals the generated value, which is the right | |
| 806 | - // answer to "does this image have its alt text?" and the wrong one to | |
| 807 | - // "how many did you change" — a re-run over a correct library reported | |
| 808 | - // every image as updated while writing nothing. `updated` and | |
| 809 | - // `skipped` have documented the write, not the return value, since | |
| 810 | - // this method was added. | |
| 811 | - $updated = count($written); | |
| 812 | - $skipped = max(0, $processed - $updated); | |
| 813 | - | |
| 814 | 682 | $next_offset = $offset + count($ids); |
| 815 | 683 | |
| 816 | 684 | // An empty batch means there is nothing left to walk, whatever the |
| 817 | 685 | // total claims. Deriving `done` from the count alone let any drift |
| @@ -834,12 +702,8 @@ | ||
| 834 | 702 | 'offset' => $offset, |
| 835 | 703 | 'next_offset' => $next_offset, |
| 836 | 704 | 'remaining' => $remaining, |
| 837 | 705 | 'done' => $exhausted || $next_offset >= $total, |
| 838 | - 'cache' => [ | |
| 839 | - 'posts_purged' => count($cache['posts']), | |
| 840 | - 'warnings' => $cache['warnings'], | |
| 841 | - ], | |
| 842 | 706 | ]; |
| 843 | 707 | } |
| 844 | 708 | |
| 845 | 709 | /** |
| @@ -879,12 +743,9 @@ | ||
| 879 | 743 | if (!wp_attachment_is_image($attachment_id)) { |
| 880 | 744 | return; |
| 881 | 745 | } |
| 882 | 746 | |
| 883 | - // No cache purge: this fires on add_attachment, so the file was created | |
| 884 | - // seconds ago and no page can be displaying it yet. Skipping the lookup | |
| 885 | - // keeps a bulk media import off two unindexed postmeta scans per file. | |
| 886 | - $this->fill_attachment_alt($attachment_id, !empty($settings['media_alt_overwrite']), false); | |
| 747 | + $this->fill_attachment_alt($attachment_id, !empty($settings['media_alt_overwrite'])); | |
| 887 | 748 | } |
| 888 | 749 | |
| 889 | 750 | /** |
| 890 | 751 | * Total number of image attachments in the library. |