Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions docs/designers-developers/developers/block-api/block-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,4 @@ registerBlockType( 'my-plugin/record-title', {

### PHP

A block's context values are available from the `context` property of the `$block` argument passed to the `render_callback` function.

`record-title/index.php`

```php
register_block_type( 'my-plugin/record-title', [
'render_callback' => function( $block ) {
return 'The current record ID is: ' . $block->context['my-plugin/recordId'];
},
] );
```
_The PHP implementation of block context is currently experimental and subject to breaking changes. It will be documented in the future once the API has stabilized._
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Because it is a dynamic block it doesn't need to override the default `save` imp
* Plugin Name: Gutenberg examples dynamic
*/

function gutenberg_examples_dynamic_render_callback( $block, $content ) {
function gutenberg_examples_dynamic_render_callback( $block_attributes, $content ) {
$recent_posts = wp_get_recent_posts( array(
'numberposts' => 1,
'post_status' => 'publish',
Expand Down Expand Up @@ -143,14 +143,6 @@ There are a few things to notice:
* The built-in `save` function just returns `null` because the rendering is performed server-side.
* The server-side rendering is a function taking the block and the block inner content as arguments, and returning the markup (quite similar to shortcodes)

Note that for convenience and for backward-compatibility, the first argument of a `render_callback` function can also be referenced as an associative array of the block's attributes:

```php
function gutenberg_examples_dynamic_render_callback( $block_attributes ) {
return 'The record ID is: ' . esc_html( $block_attributes['recordId'] );
}
```

## Live rendering in the block editor

Gutenberg 2.8 added the [`<ServerSideRender>`](/packages/server-side-render/README.md) block which enables rendering to take place on the server using PHP rather than in JavaScript.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ You can also use the post meta data in other blocks. For this example the data i
In PHP, use the [register_block_type](https://developer.wordpress.org/reference/functions/register_block_type/) function to set a callback when the block is rendered to include the meta value.

```php
function myguten_render_paragraph( $block, $content ) {
function myguten_render_paragraph( $block_attributes, $content ) {
$value = get_post_meta( get_the_ID(), 'myguten_meta_block_field', true );
// check value is set before outputting
if ( $value ) {
Expand Down
73 changes: 8 additions & 65 deletions lib/class-wp-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/**
* Class representing a parsed instance of a block.
*/
class WP_Block implements ArrayAccess {
class WP_Block {

/**
* Name of block.
Expand Down Expand Up @@ -156,7 +156,7 @@ function( $inner_block ) use ( $child_context, $registry ) {
* @return string Rendered block output.
*/
public function render() {
global $post;
global $post, $_experimental_block;

$is_dynamic = $this->name && null !== $this->block_type && $this->block_type->is_dynamic();
$block_content = '';
Expand All @@ -169,72 +169,15 @@ public function render() {
}

if ( $is_dynamic ) {
$global_post = $post;
$block_content = (string) call_user_func( $this->block_type->render_callback, $this, $block_content );
$post = $global_post;
$global_post = $post;
$global_block = $_experimental_block;
$_experimental_block = $this;
$block_content = (string) call_user_func( $this->block_type->render_callback, $this->attributes, $block_content );
$_experimental_block = $global_block;
$post = $global_post;
}

return $block_content;
}

/**
* Returns true if an attribute exists by the specified attribute name, or
* false otherwise.
*
* @link https://www.php.net/manual/en/arrayaccess.offsetexists.php
*
* @param string $attribute_name Name of attribute to check.
*
* @return bool Whether attribute exists.
*/
public function offsetExists( $attribute_name ) {
return isset( $this->attributes[ $attribute_name ] );
}

/**
* Returns the value by the specified attribute name.
*
* @link https://www.php.net/manual/en/arrayaccess.offsetget.php
*
* @param string $attribute_name Name of attribute value to retrieve.
*
* @return mixed|null Attribute value if exists, or null.
*/
public function offsetGet( $attribute_name ) {
// This may cause an "Undefined index" notice if the attribute name does
// not exist. This is expected, since the purpose of this implementation
// is to align exactly to the expectations of operating on an array.
return $this->attributes[ $attribute_name ];
}

/**
* Assign an attribute value by the specified attribute name.
*
* @link https://www.php.net/manual/en/arrayaccess.offsetset.php
*
* @param string $attribute_name Name of attribute value to set.
* @param mixed $value Attribute value.
*/
public function offsetSet( $attribute_name, $value ) {
if ( is_null( $attribute_name ) ) {
// This is not technically a valid use-case for attributes. Since
// this implementation is expected to align to expectations of
// operating on an array, it is still supported.
$this->attributes[] = $value;
} else {
$this->attributes[ $attribute_name ] = $value;
}
}

/**
* Unset an attribute.
*
* @link https://www.php.net/manual/en/arrayaccess.offsetunset.php
*
* @param string $attribute_name Name of attribute value to unset.
*/
public function offsetUnset( $attribute_name ) {
unset( $this->attributes[ $attribute_name ] );
}

}
9 changes: 4 additions & 5 deletions packages/block-library/src/post-title/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
/**
* Renders the `core/post-title` block on the server.
*
* @param WP_Block $block The block instance.
*
* @return string Returns the filtered post title for the current post wrapped inside "h1" tags.
*/
function render_block_core_post_title( $block ) {
if ( ! isset( $block->context['postId'] ) ) {
function render_block_core_post_title() {
global $_experimental_block;
if ( ! isset( $_experimental_block->context['postId'] ) ) {
return '';
}

return '<h1>' . get_the_title( $block->context['postId'] ) . '</h1>';
return '<h1>' . get_the_title( $_experimental_block->context['postId'] ) . '</h1>';
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/e2e-tests/plugins/block-context.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ function gutenberg_test_register_context_blocks() {
'gutenberg/test-context-consumer',
array(
'context' => array( 'gutenberg/recordId' ),
'render_callback' => function( $block ) {
$record_id = $block->context['gutenberg/recordId'];
'render_callback' => function() {
global $_experimental_block;

$record_id = $_experimental_block->context['gutenberg/recordId'];

if ( ! is_int( $record_id ) ) {
throw new Exception( 'Expected numeric recordId' );
Expand Down
6 changes: 4 additions & 2 deletions phpunit/class-block-context-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ function test_provides_block_context() {
'gutenberg/contextWithAssigned',
'gutenberg/contextWithoutDefault',
),
'render_callback' => function( $block ) use ( &$provided_context ) {
$provided_context[] = $block->context;
'render_callback' => function() use ( &$provided_context ) {
global $_experimental_block;

$provided_context[] = $_experimental_block->context;

return '';
},
Expand Down
45 changes: 7 additions & 38 deletions phpunit/class-wp-block-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ function test_render_static_block_type_returns_own_content() {
$this->assertSame( 'abc', $block->render() );
}

function test_render_passes_instance_to_render_callback() {
function test_render_assigns_instance_global_for_render_callback() {
$this->registry->register(
'core/greeting',
array(
Expand All @@ -259,11 +259,13 @@ function test_render_passes_instance_to_render_callback() {
'default' => '!',
),
),
'render_callback' => function( $block ) {
'render_callback' => function() {
global $_experimental_block;

return sprintf(
'Hello %s%s',
$block->attributes['toWhom'],
$block->attributes['punctuation']
$_experimental_block->attributes['toWhom'],
$_experimental_block->attributes['punctuation']
);
},
)
Expand Down Expand Up @@ -312,7 +314,7 @@ function test_passes_content_to_render_callback() {
$this->registry->register(
'core/outer',
array(
'render_callback' => function( $block, $content ) {
'render_callback' => function( $block_attributes, $content ) {
return $content;
},
)
Expand All @@ -334,37 +336,4 @@ function test_passes_content_to_render_callback() {
$this->assertSame( 'abc', $block->render() );
}

function test_array_access_attributes() {
$this->registry->register(
'core/example',
array(
'attributes' => array(
'value' => array(
'type' => 'string',
),
),
)
);
$parsed_block = array(
'blockName' => 'core/example',
'attrs' => array( 'value' => 'ok' ),
);
$context = array();
$block = new WP_Block( $parsed_block, $context, $this->registry );

$this->assertTrue( isset( $block['value'] ) );
$this->assertFalse( isset( $block['nonsense'] ) );
$this->assertEquals( 'ok', $block['value'] );

$block['value'] = 'changed';
$this->assertEquals( 'changed', $block['value'] );
$this->assertEquals( 'changed', $block->attributes['value'] );

unset( $block['value'] );
$this->assertFalse( isset( $block['value'] ) );

$block[] = 'invalid, but still supported';
$this->assertEquals( 'invalid, but still supported', $block[0] );
}

}