Make WordPress Core

Changeset 59938


Ignore:
Timestamp:
03/05/2025 10:15:09 PM (2 weeks ago)
Author:
flixos90
Message:

Editor: Fix block type and block metadata collection registration issues on Windows due to lack of path normalization.

Props flixos90, gziolo, joemcgill.
Fixes #63027.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/blocks.php

    r59874 r59938  
    446446     */
    447447
     448    $file_or_folder = wp_normalize_path( $file_or_folder );
     449
    448450    $metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ?
    449451        trailingslashit( $file_or_folder ) . 'block.json' :
    450452        $file_or_folder;
    451453
    452     $is_core_block        = str_starts_with( $file_or_folder, ABSPATH . WPINC );
     454    $is_core_block        = str_starts_with( $file_or_folder, wp_normalize_path( ABSPATH . WPINC ) );
    453455    $metadata_file_exists = $is_core_block || file_exists( $metadata_file );
    454456    $registry_metadata    = WP_Block_Metadata_Registry::get_metadata( $file_or_folder );
  • trunk/src/wp-includes/class-wp-block-metadata-registry.php

    r59874 r59938  
    8383     */
    8484    public static function register_collection( $path, $manifest ) {
    85         $path = wp_normalize_path( rtrim( $path, '/' ) );
     85        $path = rtrim( wp_normalize_path( $path ), '/' );
    8686
    8787        $collection_roots = self::get_default_collection_roots();
     
    113113            array_map(
    114114                static function ( $allowed_root ) {
    115                     return rtrim( $allowed_root, '/' );
     115                    return rtrim( wp_normalize_path( $allowed_root ), '/' );
    116116                },
    117117                $collection_roots
     
    162162     */
    163163    public static function get_metadata( $file_or_folder ) {
     164        $file_or_folder = wp_normalize_path( $file_or_folder );
     165
    164166        $path = self::find_collection_path( $file_or_folder );
    165167        if ( ! $path ) {
     
    195197     */
    196198    public static function get_collection_block_metadata_files( $path ) {
    197         $path = wp_normalize_path( rtrim( $path, '/' ) );
     199        $path = rtrim( wp_normalize_path( $path ), '/' );
    198200
    199201        if ( ! isset( self::$collections[ $path ] ) ) {
     
    214216
    215217        return array_map(
     218            // No normalization necessary since `$path` is already normalized and `$block_name` is just a folder name.
    216219            static function ( $block_name ) use ( $path ) {
    217220                return "{$path}/{$block_name}/block.json";
     
    226229     * @since 6.7.0
    227230     *
    228      * @param string $file_or_folder The path to the file or folder.
    229      * @return string|null The collection path if found, or null if not found.
     231     * @param string $file_or_folder The normalized path to the file or folder.
     232     * @return string|null The normalized collection path if found, or null if not found.
    230233     */
    231234    private static function find_collection_path( $file_or_folder ) {
     
    235238
    236239        // Check the last matched collection first, since block registration usually happens in batches per plugin or theme.
    237         $path = wp_normalize_path( rtrim( $file_or_folder, '/' ) );
     240        $path = rtrim( $file_or_folder, '/' );
    238241        if ( self::$last_matched_collection && str_starts_with( $path, self::$last_matched_collection ) ) {
    239242            return self::$last_matched_collection;
     
    280283     * @since 6.7.0
    281284     *
    282      * @param string $path The file or folder path to determine the block identifier from.
     285     * @param string $path The normalized file or folder path to determine the block identifier from.
    283286     * @return string The block identifier, or an empty string if the path is empty.
    284287     */
     
    303306     * @since 6.7.2
    304307     *
    305      * @param string   $path             Block metadata collection path, without trailing slash.
    306      * @param string[] $collection_roots List of collection root paths, without trailing slashes.
     308     * @param string   $path             Normalized block metadata collection path, without trailing slash.
     309     * @param string[] $collection_roots List of normalized collection root paths, without trailing slashes.
    307310     * @return bool True if the path is allowed, false otherwise.
    308311     */
  • trunk/tests/phpunit/tests/blocks/register.php

    r58801 r59938  
    15021502        );
    15031503    }
     1504
     1505    /**
     1506     * @ticket 63027
     1507     *
     1508     * @covers ::register_block_type_from_metadata
     1509     */
     1510    public function test_register_block_type_from_metadata_with_windows_path() {
     1511        $windows_like_path = str_replace( '/', '\\', DIR_TESTDATA ) . '\\blocks\\notice';
     1512
     1513        $this->assertNotFalse( register_block_type_from_metadata( $windows_like_path ) );
     1514    }
    15041515}
  • trunk/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php

    r59874 r59938  
    221221        );
    222222    }
     223
     224    /**
     225     * Tests that `register_collection()`, `get_metadata()`, and `get_collection_metadata_files()` handle Windows paths.
     226     *
     227     * @ticket 63027
     228     * @covers ::register_collection
     229     * @covers ::get_metadata
     230     * @covers ::get_collection_metadata_files
     231     */
     232    public function test_with_windows_paths() {
     233        // Set up a mock manifest file.
     234        $manifest_data = array(
     235            'test-block' => array(
     236                'name'  => 'test-block',
     237                'title' => 'Test Block',
     238            ),
     239        );
     240        file_put_contents( $this->temp_manifest_file, '<?php return ' . var_export( $manifest_data, true ) . ';' );
     241
     242        $plugins_path = 'C:\\Site\\wp-content\\plugins';
     243        $plugin_path  = $plugins_path . '\\my-plugin\\blocks';
     244        $block_path   = $plugin_path . '\\test-block\\block.json';
     245
     246        // Register the mock plugins directory as an allowed root.
     247        add_filter(
     248            'wp_allowed_block_metadata_collection_roots',
     249            static function ( $paths ) use ( $plugins_path ) {
     250                $paths[] = $plugins_path;
     251                return $paths;
     252            }
     253        );
     254
     255        $this->assertTrue( WP_Block_Metadata_Registry::register_collection( $plugin_path, $this->temp_manifest_file ), 'Could not register block metadata collection.' );
     256        $this->assertSame( $manifest_data['test-block'], WP_Block_Metadata_Registry::get_metadata( $block_path ), 'Could not find collection for provided block.json path.' );
     257        $this->assertSame( array( wp_normalize_path( $block_path ) ), WP_Block_Metadata_Registry::get_collection_block_metadata_files( $plugin_path ), 'Could not get correct list of block.json paths for collection.' );
     258    }
     259
     260    /**
     261     * Tests that `register_collection()` handles Windows paths correctly for verifying allowed roots.
     262     *
     263     * @ticket 63027
     264     * @covers ::register_collection
     265     */
     266    public function test_with_windows_paths_and_disallowed_location() {
     267        $parent_path  = 'C:\\Site\\wp-content';
     268        $plugins_path = $parent_path . '\\plugins';
     269        $plugin_path  = $plugins_path . '\\my-plugin\\blocks';
     270
     271        // Register the mock plugins directory as an allowed root.
     272        add_filter(
     273            'wp_allowed_block_metadata_collection_roots',
     274            static function ( $paths ) use ( $plugins_path ) {
     275                $paths[] = $plugins_path;
     276                return $paths;
     277            }
     278        );
     279
     280        $this->setExpectedIncorrectUsage( 'WP_Block_Metadata_Registry::register_collection' );
     281
     282        $result = WP_Block_Metadata_Registry::register_collection( $plugins_path, $this->temp_manifest_file );
     283        $this->assertFalse( $result, 'Arbitrary Windows path should not be registered if it matches a collection root' );
     284
     285        $result = WP_Block_Metadata_Registry::register_collection( $parent_path, $this->temp_manifest_file );
     286        $this->assertFalse( $result, 'Arbitrary Windows path should not be registered if it is a parent directory of a collection root' );
     287
     288        $result = WP_Block_Metadata_Registry::register_collection( $plugin_path, $this->temp_manifest_file );
     289        $this->assertTrue( $result, 'Arbitrary Windows path should be registered successfully if it is within a collection root' );
     290    }
    223291}
Note: See TracChangeset for help on using the changeset viewer.