Make WordPress Core

Opened 2 years ago

Last modified 3 months ago

#62407 new defect (bug)

PHP Notice: Undefined offset in WordPress core when using template registered in a plugin via register_block_template() in WordPress 6.7

Reported by: Grigory Volochiy Owned by:
Priority: normal Milestone: Future Release
Component: General Version: 6.7
Severity: normal Keywords: has-dev-note has-patch has-unit-tests early
Cc: Focuses: template

Description

Issue Description
With the release of WordPress 6.7, the register_block_template() function allows plugins to register block templates. While this functionality operates as intended, it triggers a PHP notice about an undefined offset within the WordPress core.

Affected file: wp-includes/block-editor.php
Line: 467
Code snippet: {{{#!php
<?php
$template_blocks = parse_blocks( $current_template[0]->content );
}}}

Cause of the Issue
The get_block_templates() function now returns an array with a mix of numerical keys and associative keys where the associative keys are the registered template IDs via plugin (e.g., 'my-pluginmy-template') instead of numerical keys.
The core code at block-editor.php line 467 expects $current_template to be a numerically indexed array.
When an associative key is encountered, it leads to a PHP notice: Notice: Undefined offset: 0 in /wp-includes/block-editor.php on line 467.

Impact
PHP Notice:
Notice: Undefined offset: 0 in /wp-includes/block-editor.php on line 467


Compatibility Issues:
Existing plugin code that relies on numerical keys within the templates array does not anticipate associative keys, potentially causing further functional discrepancies beyond the PHP notice.


Steps to Reproduce

Environment Setup:
WordPress Version: 6.7
PHP Version: [PHP 7.4.33 (or higher)]
Active Theme: [Twenty Twenty-Five (the notice appears with other themes too)]

Procedure:
Create a plugin that registers a block template using register_block_template().

<?php
function add_template() {

        $template_id = 'my-plugin//my-template';
        $existing_template = get_block_template( $template_id );

        if ( $existing_template || is_wp_error( $existing_template ) ) {
                return;
        }

        $template_content =
                '<!-- wp:template-part {"slug":"header"} /-->
                <!-- wp:post-content {"lock":{"remove":true}} /-->
                <!-- wp:template-part {"slug":"footer"} /-->';

        register_block_template( $template_id, [
                'title' => __( 'Template title', 'my-plugin' ),
                'description' => __( 'Template description', 'my-plugin' ),
                'content' => $template_content,
                'post_types'  => array( 'page' ),
        ] );
}



Ensure DEBUG is set to true in wp-config.php to see the notice printed in HTML.
Navigate to Pages -> Add New Page. Assign newly created template to the page. Publish the page. Reload the editor page.


Observation:
A PHP notice appears in the page HTML (it appears briefly in the editor and is better visible in browser developer tools when viewing the source HTML of the current page)

Notice: Undefined offset: 0 in /wp-includes/block-editor.php on line 467
And as additional result next PHP notice:
Trying to get property 'content' of non-object in /wp-includes/block-editor.php on line 467

Expected Behavior
The WordPress core should handle templates registered via register_block_template() without triggering any PHP notices or errors. Specifically, the get_block_templates() function should return a numerically indexed array compatible with existing code expectations.

Actual Behavior
A PHP notice about an undefined offset is displayed, indicating that get_block_templates() returns an associative array with template IDs as keys instead of a numerically indexed array. This discrepancy leads to compatibility issues with existing code that expects numerical keys.

Attachments (3)

Screenshot 2024-11-13 131326.png​ (50.2 KB ) - added by Grigory Volochiy 2 years ago.
The PHP notices visible in the HTML source of the page editor.
62407-pattern-scrolling.gif​ (613.4 KB ) - added by adrianduffell 3 months ago.
Excessive margins between patterns
testing-62407.zip​ (1.8 KB ) - added by adrianduffell 3 months ago.
Minimal plugin to reproduce the issue

Download all attachments as: .zip

Change History (14)

@Grigory Volochiy
2 years ago

The PHP notices visible in the HTML source of the page editor.

This ticket was mentioned in ​PR #8086 on ​WordPress/wordpress-develop by ​@lgersman.


22 months ago
#1

  • Keywords has-patch added; needs-patch removed

This ticket fixes using templates registered by register_block_template() in the Gutenberg editor.

Function get_block_templates() tries to accumulate registered block templates from the Block Template Registry

The problem is that the Block Template Registry call get_by_query( $query ) returns an associative array but the code expects a indexed array.

Trac ticket: https://core.trac.wordpress.org/ticket/62407

Could you have a look into it, @noisysocks ?

​@lgersman commented on ​PR #8086:


22 months ago
#2

Hey @noisysocks,

I fixed the failing test test_get_block_templates_from_registry.

The documentation of the get_block_templates function does not clearly express whether an index-based or associative array will be returned.
I assume that get_block_templates should return either an index based OR an associative (key based) array.

Gutenberg assumes an index based array (see bug https://core.trac.wordpress.org/ticket/62407).
In any case, I found this more sensible than an array with mixed (!) indices as in the current implementation.

The bugfix and the corresponding test case have been adapted accordingly.

The one (gracefully) failing e2e check (​https://github.com/WordPress/wordpress-develop/actions/runs/12610283240/job/35144694194) has nothing to do with the proposed check. I assume it's just an timeout.

Cheers,

Lars

@adrianduffell
3 months ago

Excessive margins between patterns

#3 follow-up: @adrianduffell
3 months ago

  • Milestone Awaiting Review → 7.1

I could reproduce the notices and this seems to also cause a visual defect in the pattern selector, creating an excessive margin between patterns (see attached).

Though the beta is fast approaching, I think it would be worth trying to fix in the 7.1 cycle to avoid friction for devs adopting register_block_template().

@adrianduffell
3 months ago

Minimal plugin to reproduce the issue

This ticket was mentioned in ​PR #12476 on ​WordPress/wordpress-develop by ​@adrianduffell.


3 months ago
#4

  • Keywords has-unit-tests added

Fixes an undefined offset notice in wp_get_post_content_block_attributes() by selecting the first array item agnostically using array_key_first(). This results in a visual defect in the editor‘s pattern selector when WP_DEBUG and WP_DEBUG_DISPLAY is true.

Previously wp_get_post_content_block_attributes() expected the array returned by get_block_templates() to have numeric keys and would select the first item using index 0. However, when a custom block template is added register_block_template(), an associative array is returned instead, causing the 0 index to be undefined. By using array_key_first(), the first item can be selected no matter whether a numeric or associative array is returned. This function was introduced in PHP 7.3.

Trac ticket: https://core.trac.wordpress.org/ticket/62407

## Use of AI Tools

AI assistance: Yes
Tool(s): Codex
Model(s): 5.6 Sol Light
Used for: Generating the unit test; final implementation and tests were reviewed and edited by me.

#5 @misulicus
3 months ago

#65609 was marked as a duplicate.

#6 in reply to: ↑ 3 @misulicus
3 months ago

We hit this in Easy Digital Downloads after registering a page template via register_block_template() (easy-digital-downloads//page-checkout, post_types => array( 'page' )). On any block theme without a matching theme file for the slug, editing any page in the block editor logs four entries per editor load:

PHP Warning:  Undefined array key 0 in wp-includes/block-editor.php on line 469
PHP Warning:  Attempt to read property "content" on null in wp-includes/block-editor.php on line 469
PHP Deprecated:  preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated in wp-includes/class-wp-block-parser.php on line 247
PHP Deprecated:  strlen(): Passing null to parameter #1 ($string) of type string is deprecated in wp-includes/class-wp-block-parser.php on line 324

The deprecations follow from the warnings: $current_template[0] resolves to null, so parse_blocks( null ) feeds null into WP_Block_Parser.

Root cause matches the ticket description: WP_Block_Templates_Registry::get_by_query() returns templates keyed by the full template name string, and get_block_templates() merges them with array_merge(), which preserves string keys — so when the theme provides no file for the queried slug, the returned array is non-empty but has no index 0.

WooCommerce's registered page-checkout template hit the same pattern in the template-activate flow (Gutenberg #72786 / PR #72795 — that fix is in the plugin's compat layer and doesn't address this core path). Every plugin adopting the 6.7 registration API on a block theme is likely to trip over this, so +1 for landing a fix in 7.1.

Normalizing inside get_block_templates() (as PR #8086 does) seems preferable to only hardening wp_get_post_content_block_attributes(): the documented return type is WP_Block_Template[] and the string keys also leak into the get_block_templates filter and any other consumer indexing numerically.

#7 @adrianduffell
3 months ago

Thanks for sharing those details, @misulicus!

Normalizing inside get_block_templates() (as PR #8086 does) seems preferable to only hardening wp_get_post_content_block_attributes(): the documented return type is WP_Block_Template[] and the string keys also leak into the get_block_templates filter and any other consumer indexing numerically.

Ah right, I've interpreted WP_Block_Template[] to mean any kind of array when creating PR 12476 ​https://github.com/WordPress/wordpress-develop/pull/12476. If the intention of get_block_templates() is to return numeric arrays only, I agree normalizing would be best.

This ticket was mentioned in ​Slack in #core by adrianduffell. ​View the logs.


3 months ago

This ticket was mentioned in ​Slack in #core by audrasjb. ​View the logs.


3 months ago

This ticket was mentioned in ​Slack in #core by adrianduffell. ​View the logs.


3 months ago

#11 @adrianduffell
3 months ago

  • Keywords early added
  • Milestone 7.1 → Future Release

This was discussed in today’s bug scrub. Alas with no recent activity, I think it is best to punt with early keyword for consideration in 7.2.

Note: See TracTickets for help on using tickets.