<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[57641] trunk: Editor: Merge `uses_context` defined by block bindings sources with block types</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/57641">57641</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/57641","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>gziolo</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-02-16 12:53:16 +0000 (Fri, 16 Feb 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Editor: Merge `uses_context` defined by block bindings sources with block types

Adds logic that fixes the limitation for souces by allowing merging the `uses_context` defined by block bindings sources into supported block types. Each source defines the context it needs and it is added to the block types that are using the block bindings API.

Fixes <a href="https://core.trac.wordpress.org/ticket/60525">#60525</a>.
Props santosguillamot, gziolo, czapla, thekt12.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesblockbindingspatternoverridesphp">trunk/src/wp-includes/block-bindings/pattern-overrides.php</a></li>
<li><a href="#trunksrcwpincludesblockbindingspostmetaphp">trunk/src/wp-includes/block-bindings/post-meta.php</a></li>
<li><a href="#trunksrcwpincludesblockbindingsphp">trunk/src/wp-includes/block-bindings.php</a></li>
<li><a href="#trunksrcwpincludesclasswpblockbindingsregistryphp">trunk/src/wp-includes/class-wp-block-bindings-registry.php</a></li>
<li><a href="#trunksrcwpincludesclasswpblockbindingssourcephp">trunk/src/wp-includes/class-wp-block-bindings-source.php</a></li>
<li><a href="#trunksrcwpincludesclasswpblocktypephp">trunk/src/wp-includes/class-wp-block-type.php</a></li>
<li><a href="#trunksrcwpincludesclasswpblockphp">trunk/src/wp-includes/class-wp-block.php</a></li>
<li><a href="#trunktestsphpunittestsblockbindingsrenderphp">trunk/tests/phpunit/tests/block-bindings/render.php</a></li>
<li><a href="#trunktestsphpunittestsblockbindingswpBlockBindingsRegistryphp">trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesblockbindingspatternoverridesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/block-bindings/pattern-overrides.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-bindings/pattern-overrides.php        2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/block-bindings/pattern-overrides.php  2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -39,6 +39,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'label'              => _x( 'Pattern Overrides', 'block bindings source' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'get_value_callback' => '_block_bindings_pattern_overrides_get_value',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'uses_context'       => array( 'pattern/overrides' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 )
</span><span class="cx" style="display: block; padding: 0 10px">        );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpincludesblockbindingspostmetaphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/block-bindings/post-meta.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-bindings/post-meta.php        2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/block-bindings/post-meta.php  2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -13,22 +13,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 6.5.0
</span><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param array $source_args Array containing source arguments used to look up the override value.
- *                           Example: array( "key" => "foo" ).
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param array    $source_args    Array containing source arguments used to look up the override value.
+ *                                 Example: array( "key" => "foo" ).
+ * @param WP_Block $block_instance The block instance.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @return mixed The value computed for the source.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function _block_bindings_post_meta_get_value( array $source_args ) {
-       if ( ! isset( $source_args['key'] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function _block_bindings_post_meta_get_value( array $source_args, $block_instance ) {
+       if ( empty( $source_args['key'] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 return null;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        // Use the postId attribute if available.
-       if ( isset( $source_args['postId'] ) ) {
-               $post_id = $source_args['postId'];
-       } else {
-               // $block_instance->context['postId'] is not available in the Image block.
-               $post_id = get_the_ID();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( empty( $block_instance->context['postId'] ) ) {
+               return null;
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        $post_id = $block_instance->context['postId'];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // If a post isn't public, we need to prevent unauthorized users from accessing the post meta.
</span><span class="cx" style="display: block; padding: 0 10px">        $post = get_post( $post_id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -51,6 +49,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'label'              => _x( 'Post Meta', 'block bindings source' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'get_value_callback' => '_block_bindings_post_meta_get_value',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'uses_context'       => array( 'postId', 'postType' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 )
</span><span class="cx" style="display: block; padding: 0 10px">        );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpincludesblockbindingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/block-bindings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-bindings.php  2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/block-bindings.php    2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -75,18 +75,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param array  $source_properties {
</span><span class="cx" style="display: block; padding: 0 10px">  *     The array of arguments that are used to register a source.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- *     @type string   $label              The label of the source.
- *     @type callback $get_value_callback A callback executed when the source is processed during block rendering.
- *                                        The callback should have the following signature:
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *     @type string   $label                   The label of the source.
+ *     @type callback $get_value_callback      A callback executed when the source is processed during block rendering.
+ *                                             The callback should have the following signature:
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- *                                        `function ($source_args, $block_instance,$attribute_name): mixed`
- *                                            - @param array    $source_args    Array containing source arguments
- *                                                                              used to look up the override value,
- *                                                                              i.e. {"key": "foo"}.
- *                                            - @param WP_Block $block_instance The block instance.
- *                                            - @param string   $attribute_name The name of an attribute .
- *                                        The callback has a mixed return type; it may return a string to override
- *                                        the block's original value, null, false to remove an attribute, etc.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *                                             `function ($source_args, $block_instance,$attribute_name): mixed`
+ *                                                 - @param array    $source_args    Array containing source arguments
+ *                                                                                   used to look up the override value,
+ *                                                                                   i.e. {"key": "foo"}.
+ *                                                 - @param WP_Block $block_instance The block instance.
+ *                                                 - @param string   $attribute_name The name of an attribute .
+ *                                             The callback has a mixed return type; it may return a string to override
+ *                                             the block's original value, null, false to remove an attribute, etc.
+ *     @type array    $uses_context (optional) Array of values to add to block `uses_context` needed by the source.
</ins><span class="cx" style="display: block; padding: 0 10px">  * }
</span><span class="cx" style="display: block; padding: 0 10px">  * @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpblockbindingsregistryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-block-bindings-registry.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-block-bindings-registry.php        2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/class-wp-block-bindings-registry.php  2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -33,6 +33,31 @@
</span><span class="cx" style="display: block; padding: 0 10px">        private static $instance = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Supported source properties that can be passed to the registered source.
+        *
+        * @since 6.5.0
+        * @var array
+        */
+       private $allowed_source_properties = array(
+               'label',
+               'get_value_callback',
+               'uses_context',
+       );
+
+       /**
+        * Supported blocks that can use the block bindings API.
+        *
+        * @since 6.5.0
+        * @var array
+        */
+       private $supported_blocks = array(
+               'core/paragraph',
+               'core/heading',
+               'core/image',
+               'core/button',
+       );
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Registers a new block bindings source.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * This is a low-level method. For most use cases, it is recommended to use
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -53,18 +78,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array    $source_properties {
</span><span class="cx" style="display: block; padding: 0 10px">         *     The array of arguments that are used to register a source.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         *     @type string   $label              The label of the source.
-        *     @type callback $get_value_callback A callback executed when the source is processed during block rendering.
-        *                                        The callback should have the following signature:
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  *     @type string   $label                   The label of the source.
+        *     @type callback $get_value_callback      A callback executed when the source is processed during block rendering.
+        *                                             The callback should have the following signature:
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         *                                        `function ($source_args, $block_instance,$attribute_name): mixed`
-        *                                            - @param array    $source_args    Array containing source arguments
-        *                                                                              used to look up the override value,
-        *                                                                              i.e. {"key": "foo"}.
-        *                                            - @param WP_Block $block_instance The block instance.
-        *                                            - @param string   $attribute_name The name of the target attribute.
-        *                                        The callback has a mixed return type; it may return a string to override
-        *                                        the block's original value, null, false to remove an attribute, etc.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  *                                             `function ($source_args, $block_instance,$attribute_name): mixed`
+        *                                                 - @param array    $source_args    Array containing source arguments
+        *                                                                                   used to look up the override value,
+        *                                                                                   i.e. {"key": "foo"}.
+        *                                                 - @param WP_Block $block_instance The block instance.
+        *                                                 - @param string   $attribute_name The name of the target attribute.
+        *                                             The callback has a mixed return type; it may return a string to override
+        *                                             the block's original value, null, false to remove an attribute, etc.
+        *     @type array    $uses_context (optional) Array of values to add to block `uses_context` needed by the source.
</ins><span class="cx" style="display: block; padding: 0 10px">          * }
</span><span class="cx" style="display: block; padding: 0 10px">         * @return WP_Block_Bindings_Source|false Source when the registration was successful, or `false` on failure.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -107,7 +133,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return false;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /* Validate that the source properties contain the label */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Validates that the source properties contain the label.
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! isset( $source_properties['label'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        _doing_it_wrong(
</span><span class="cx" style="display: block; padding: 0 10px">                                __METHOD__,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -117,7 +143,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return false;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /* Validate that the source properties contain the get_value_callback */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Validates that the source properties contain the get_value_callback.
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! isset( $source_properties['get_value_callback'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        _doing_it_wrong(
</span><span class="cx" style="display: block; padding: 0 10px">                                __METHOD__,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -127,7 +153,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return false;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /* Validate that the get_value_callback is a valid callback */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Validates that the get_value_callback is a valid callback.
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! is_callable( $source_properties['get_value_callback'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        _doing_it_wrong(
</span><span class="cx" style="display: block; padding: 0 10px">                                __METHOD__,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -137,6 +163,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return false;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Validates that the uses_context parameter is an array.
+               if ( isset( $source_properties['uses_context'] ) && ! is_array( $source_properties['uses_context'] ) ) {
+                       _doing_it_wrong(
+                               __METHOD__,
+                               __( 'The "uses_context" parameter must be an array.' ),
+                               '6.5.0'
+                       );
+                       return false;
+               }
+
+               if ( ! empty( array_diff( array_keys( $source_properties ), $this->allowed_source_properties ) ) ) {
+                       _doing_it_wrong(
+                               __METHOD__,
+                               __( 'The $source_properties array contains invalid properties.' ),
+                               '6.5.0'
+                       );
+                       return false;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $source = new WP_Block_Bindings_Source(
</span><span class="cx" style="display: block; padding: 0 10px">                        $source_name,
</span><span class="cx" style="display: block; padding: 0 10px">                        $source_properties
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -144,6 +189,20 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->sources[ $source_name ] = $source;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Adds `uses_context` defined by block bindings sources.
+               add_filter(
+                       'get_block_type_uses_context',
+                       function ( $uses_context, $block_type ) use ( $source ) {
+                               if ( ! in_array( $block_type->name, $this->supported_blocks, true ) || empty( $source->uses_context ) ) {
+                                       return $uses_context;
+                               }
+                               // Use array_values to reset the array keys.
+                               return array_values( array_unique( array_merge( $uses_context, $source->uses_context ) ) );
+                       },
+                       10,
+                       2
+               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 return $source;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpblockbindingssourcephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-block-bindings-source.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-block-bindings-source.php  2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/class-wp-block-bindings-source.php    2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -46,6 +46,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">        private $get_value_callback;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * The context added to the blocks needed by the source.
+        *
+        * @since 6.5.0
+        * @var array|null
+        */
+       public $uses_context = null;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Constructor.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Do not use this constructor directly. Instead, use the
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -57,9 +65,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array  $source_properties  The properties of the source.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function __construct( string $name, array $source_properties ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->name               = $name;
-               $this->label              = $source_properties['label'];
-               $this->get_value_callback = $source_properties['get_value_callback'];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->name = $name;
+               foreach ( $source_properties as $property_name => $property_value ) {
+                       $this->$property_name = $property_value;
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpblocktypephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-block-type.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-block-type.php     2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/class-wp-block-type.php       2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -180,7 +180,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.5.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @var string[]
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public $uses_context = array();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private $uses_context = array();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Context provided by blocks of this type.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -366,6 +366,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return $this->get_variations();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( 'uses_context' === $name ) {
+                       return $this->get_uses_context();
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -394,7 +398,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *              or false otherwise.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function __isset( $name ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'variations' === $name ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( in_array( $name, array( 'variations', 'uses_context' ), true ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return true;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -417,11 +421,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @param mixed  $value Property value.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function __set( $name, $value ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'variations' === $name ) {
-                       $this->variations = $value;
-                       return;
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 if ( ! in_array( $name, $this->deprecated_properties, true ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->{$name} = $value;
</span><span class="cx" style="display: block; padding: 0 10px">                        return;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -616,4 +615,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                return apply_filters( 'get_block_type_variations', $this->variations, $this );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Get block uses context.
+        *
+        * @since 6.5.0
+        *
+        * @return array[]
+        */
+       public function get_uses_context() {
+               /**
+                * Filters the registered uses context for a block type.
+                *
+                * @since 6.5.0
+                *
+                * @param array         $uses_context Array of registered uses context for a block type.
+                * @param WP_Block_Type $block_type   The full block type object.
+                */
+               return apply_filters( 'get_block_type_uses_context', $this->uses_context, $this );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpblockphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-block.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-block.php  2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/src/wp-includes/class-wp-block.php    2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -231,13 +231,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array The computed block attributes for the provided block bindings.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        private function process_block_bindings() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $parsed_block = $this->parsed_block;
-
-               $computed_attributes = array();
-
-               // Allowed blocks that support block bindings.
-               // TODO: Look for a mechanism to opt-in for this. Maybe adding a property to block attributes?
-               $allowed_blocks = array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $parsed_block               = $this->parsed_block;
+               $computed_attributes        = array();
+               $supported_block_attributes = array(
</ins><span class="cx" style="display: block; padding: 0 10px">                         'core/paragraph' => array( 'content' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'core/heading'   => array( 'content' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'core/image'     => array( 'url', 'title', 'alt' ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -244,10 +240,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'core/button'    => array( 'url', 'text', 'linkTarget', 'rel' ),
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // If the block doesn't have the bindings property, isn't one of the allowed
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // If the block doesn't have the bindings property, isn't one of the supported
</ins><span class="cx" style="display: block; padding: 0 10px">                 // block types, or the bindings property is not an array, return the block content.
</span><span class="cx" style="display: block; padding: 0 10px">                if (
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        ! isset( $allowed_blocks[ $this->name ] ) ||
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 ! isset( $supported_block_attributes[ $this->name ] ) ||
</ins><span class="cx" style="display: block; padding: 0 10px">                         empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
</span><span class="cx" style="display: block; padding: 0 10px">                        ! is_array( $parsed_block['attrs']['metadata']['bindings'] )
</span><span class="cx" style="display: block; padding: 0 10px">                ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -255,8 +251,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $parsed_block['attrs']['metadata']['bindings'] as $attribute_name => $block_binding ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // If the attribute is not in the allowed list, process next attribute.
-                       if ( ! in_array( $attribute_name, $allowed_blocks[ $this->name ], true ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 // If the attribute is not in the supported list, process next attribute.
+                       if ( ! in_array( $attribute_name, $supported_block_attributes[ $this->name ], true ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 continue;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                        // If no source is provided, or that source is not registered, process next attribute.
</span></span></pre></div>
<a id="trunktestsphpunittestsblockbindingsrenderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/block-bindings/render.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/block-bindings/render.php       2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/tests/phpunit/tests/block-bindings/render.php 2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -115,6 +115,49 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Tests passing `uses_context` as argument to the source.
+        *
+        * @ticket 60525
+        *
+        * @covers ::register_block_bindings_source
+        */
+       public function test_passing_uses_context_to_source() {
+               $get_value_callback = function ( $source_args, $block_instance, $attribute_name ) {
+                       $value = $block_instance->context['sourceContext'];
+                       return "Value: $value";
+               };
+
+               register_block_bindings_source(
+                       self::SOURCE_NAME,
+                       array(
+                               'label'              => self::SOURCE_LABEL,
+                               'get_value_callback' => $get_value_callback,
+                               'uses_context'       => array( 'sourceContext' ),
+                       )
+               );
+
+               $block_content = <<<HTML
+<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source", "args": {"key": "test"}}}}} -->
+<p>This should not appear</p>
+<!-- /wp:paragraph -->
+HTML;
+               $parsed_blocks = parse_blocks( $block_content );
+               $block         = new WP_Block( $parsed_blocks[0], array( 'sourceContext' => 'source context value' ) );
+               $result        = $block->render();
+
+               $this->assertSame(
+                       'Value: source context value',
+                       $block->attributes['content'],
+                       "The 'content' should be updated with the value of the source context."
+               );
+               $this->assertSame(
+                       '<p>Value: source context value</p>',
+                       trim( $result ),
+                       'The block content should be updated with the value of the source context.'
+               );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Tests if the block content is updated with the value returned by the source
</span><span class="cx" style="display: block; padding: 0 10px">         * for the Image block in the placeholder state.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span></span></pre></div>
<a id="trunktestsphpunittestsblockbindingswpBlockBindingsRegistryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php      2024-02-16 11:55:34 UTC (rev 57640)
+++ trunk/tests/phpunit/tests/block-bindings/wpBlockBindingsRegistry.php        2024-02-16 12:53:16 UTC (rev 57641)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -39,6 +39,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'get_value_callback' => function () {
</span><span class="cx" style="display: block; padding: 0 10px">                                return 'test-value';
</span><span class="cx" style="display: block; padding: 0 10px">                        },
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'uses_context'       => array( 'sourceContext' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -163,6 +164,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Should reject block bindings registration if `uses_context` is not an array.
+        *
+        * @ticket 60525
+        *
+        * @covers WP_Block_Bindings_Registry::register
+        *
+        * @expectedIncorrectUsage WP_Block_Bindings_Registry::register
+        */
+       public function test_register_invalid_string_uses_context() {
+
+               self::$test_source_properties['uses_context'] = 'not-an-array';
+
+               $result = $this->registry->register( self::$test_source_name, self::$test_source_properties );
+               $this->assertFalse( $result );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Should accept valid block binding source.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 60282
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -179,6 +197,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                        $result
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->assertSame( 'test/source', $result->name );
+               $this->assertSame( 'Test source', $result->label );
+               $this->assertSame(
+                       'test-value',
+                       $result->get_value( array(), null, '' )
+               );
+               $this->assertEquals( array( 'sourceContext' ), $result->uses_context );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -321,4 +346,49 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $result = $this->registry->is_registered( self::$test_source_name );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertTrue( $result );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Tests merging `uses_context` from multiple sources.
+        *
+        * @ticket 60525
+        *
+        * @covers ::register_block_bindings_source
+        * @covers WP_Block_Type::get_uses_context
+        */
+       public function test_merging_uses_context_from_multiple_sources() {
+               $get_value_callback = function () {
+                       return 'Anything';
+               };
+
+               $block_registry        = WP_Block_Type_Registry::get_instance();
+               $original_uses_context = $block_registry->get_registered( 'core/paragraph' )->uses_context;
+
+               register_block_bindings_source(
+                       'test/source-one',
+                       array(
+                               'label'              => 'Test Source One',
+                               'get_value_callback' => $get_value_callback,
+                               'uses_context'       => array( 'commonContext', 'sourceOneContext' ),
+                       )
+               );
+
+               register_block_bindings_source(
+                       'test/source-two',
+                       array(
+                               'label'              => 'Test Source Two',
+                               'get_value_callback' => $get_value_callback,
+                               'uses_context'       => array( 'commonContext', 'sourceTwoContext' ),
+                       )
+               );
+
+               $new_uses_context = $block_registry->get_registered( 'core/paragraph' )->uses_context;
+               // Checks that the resulting `uses_context` contains the values from both sources.
+               $this->assertContains( 'commonContext', $new_uses_context );
+               $this->assertContains( 'sourceOneContext', $new_uses_context );
+               $this->assertContains( 'sourceTwoContext', $new_uses_context );
+               // Checks that the resulting `uses_context` added 3 unique items.
+               $this->assertSame( count( $original_uses_context ) + 3, count( $new_uses_context ) );
+               // Checks that the array isn't sparse to prevent issues in the editor.
+               $this->assertSame( array_key_last( $new_uses_context ), count( $new_uses_context ) - 1 );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>