From 2577a225f6153c2db79ca1da1612f7dc9cfd78cf Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Fri, 20 Feb 2026 22:49:00 +0700 Subject: [PATCH 1/2] REST API: Make `filter_wp_unique_filename()` static to match core After WordPress/wordpress-develop#10868 (r61703), the core `WP_REST_Attachments_Controller::filter_wp_unique_filename()` method was changed to `private static`. This causes a fatal error in the Gutenberg subclass which declares it as non-static. Aligns the Gutenberg method signature with core by making it static and simplifying the parameters to match. Co-Authored-By: Claude Opus 4.6 --- ...s-gutenberg-rest-attachments-controller.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/media/class-gutenberg-rest-attachments-controller.php b/lib/media/class-gutenberg-rest-attachments-controller.php index 60b2930bec9b6e..6edb2c0e0ffc29 100644 --- a/lib/media/class-gutenberg-rest-attachments-controller.php +++ b/lib/media/class-gutenberg-rest-attachments-controller.php @@ -251,16 +251,14 @@ public function sideload_item_permissions_check( $request ) { * * @link https://github.com/WordPress/wordpress-develop/blob/30954f7ac0840cfdad464928021d7f380940c347/src/wp-includes/functions.php#L2576-L2582 * - * @param string $filename Unique file name. - * @param string $ext File extension. Example: ".png". - * @param string $dir Directory path. - * @param callable|null $unique_filename_callback Callback function that generates the unique file name. - * @param string[] $alt_filenames Array of alternate file names that were checked for collisions. - * @param int|string $number The highest number that was used to make the file name unique - * or an empty string if unused. + * @param string $filename Unique file name. + * @param string $dir Directory path. + * @param int|string $number The highest number that was used to make the file name unique + * or an empty string if unused. + * @param string $attachment_filename Original attachment file name. * @return string Filtered file name. */ - private function filter_wp_unique_filename( $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number, $attachment_filename ) { + private static function filter_wp_unique_filename( $filename, $dir, $number, $attachment_filename ) { if ( empty( $number ) || ! $attachment_filename ) { return $filename; } @@ -338,8 +336,8 @@ public function sideload_item( WP_REST_Request $request ) { * or an empty string if unused. * @return string Filtered file name. */ - $filter_filename = function ( $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number ) use ( $attachment_filename ) { - return $this->filter_wp_unique_filename( $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number, $attachment_filename ); + $filter_filename = static function ( $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number ) use ( $attachment_filename ) { + return self::filter_wp_unique_filename( $filename, $dir, $number, $attachment_filename ); }; add_filter( 'wp_unique_filename', $filter_filename, 10, 6 ); From 5061dba17b0877bee969f5eb84eb3a6c577cf556 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Fri, 20 Feb 2026 22:56:46 +0700 Subject: [PATCH 2/2] Avoid duplicate sideload route registration when core already has it Since core trunk now registers the sideload route in `WP_REST_Attachments_Controller::register_routes()`, only register it in the Gutenberg subclass when the parent class doesn't already have the `sideload_item` method. Updates the test accordingly. Co-Authored-By: Claude Opus 4.6 --- ...-gutenberg-rest-attachments-controller.php | 61 ++++++++++--------- ...nberg-rest-attachments-controller-test.php | 3 +- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/lib/media/class-gutenberg-rest-attachments-controller.php b/lib/media/class-gutenberg-rest-attachments-controller.php index 6edb2c0e0ffc29..b457dd469bcdf6 100644 --- a/lib/media/class-gutenberg-rest-attachments-controller.php +++ b/lib/media/class-gutenberg-rest-attachments-controller.php @@ -20,38 +20,41 @@ class Gutenberg_REST_Attachments_Controller extends WP_REST_Attachments_Controll public function register_routes(): void { parent::register_routes(); - $valid_image_sizes = array_keys( wp_get_registered_image_subsizes() ); - - // Special case to set 'original_image' in attachment metadata. - $valid_image_sizes[] = 'original'; - // Used for PDF thumbnails. - $valid_image_sizes[] = 'full'; - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)/sideload', - array( + // Only register the sideload route if the parent class doesn't already have it. + if ( ! method_exists( get_parent_class( $this ), 'sideload_item' ) ) { + $valid_image_sizes = array_keys( wp_get_registered_image_subsizes() ); + + // Special case to set 'original_image' in attachment metadata. + $valid_image_sizes[] = 'original'; + // Used for PDF thumbnails. + $valid_image_sizes[] = 'full'; + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\d]+)/sideload', array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'sideload_item' ), - 'permission_callback' => array( $this, 'sideload_item_permissions_check' ), - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the attachment.', 'gutenberg' ), - 'type' => 'integer', - ), - 'image_size' => array( - 'description' => __( 'Image size.', 'gutenberg' ), - 'type' => 'string', - 'enum' => $valid_image_sizes, - 'required' => true, + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'sideload_item' ), + 'permission_callback' => array( $this, 'sideload_item_permissions_check' ), + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the attachment.', 'gutenberg' ), + 'type' => 'integer', + ), + 'image_size' => array( + 'description' => __( 'Image size.', 'gutenberg' ), + 'type' => 'string', + 'enum' => $valid_image_sizes, + 'required' => true, + ), ), ), - ), - 'allow_batch' => $this->allow_batch, - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); + 'allow_batch' => $this->allow_batch, + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } } /** diff --git a/phpunit/media/class-gutenberg-rest-attachments-controller-test.php b/phpunit/media/class-gutenberg-rest-attachments-controller-test.php index 31f2b51c6eb350..3a31dab4538fea 100644 --- a/phpunit/media/class-gutenberg-rest-attachments-controller-test.php +++ b/phpunit/media/class-gutenberg-rest-attachments-controller-test.php @@ -46,7 +46,8 @@ public function test_register_routes() { $this->assertArrayHasKey( '/wp/v2/media/(?P[\d]+)', $routes ); $this->assertCount( 3, $routes['/wp/v2/media/(?P[\d]+)'] ); $this->assertArrayHasKey( '/wp/v2/media/(?P[\d]+)/sideload', $routes ); - $this->assertCount( 1, $routes['/wp/v2/media/(?P[\d]+)/sideload'] ); + // Core may already register the sideload route; Gutenberg only adds it when core doesn't have it. + $this->assertGreaterThanOrEqual( 1, count( $routes['/wp/v2/media/(?P[\d]+)/sideload'] ) ); } public function test_get_items() {