class-wp-block.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <?php
  2. /**
  3. * Blocks API: WP_Block class
  4. *
  5. * @package WordPress
  6. * @since 5.5.0
  7. */
  8. /**
  9. * Class representing a parsed instance of a block.
  10. *
  11. * @since 5.5.0
  12. * @property array $attributes
  13. */
  14. #[AllowDynamicProperties]
  15. class WP_Block {
  16. /**
  17. * Original parsed array representation of block.
  18. *
  19. * @since 5.5.0
  20. * @var array
  21. */
  22. public $parsed_block;
  23. /**
  24. * Name of block.
  25. *
  26. * @example "core/paragraph"
  27. *
  28. * @since 5.5.0
  29. * @var string
  30. */
  31. public $name;
  32. /**
  33. * Block type associated with the instance.
  34. *
  35. * @since 5.5.0
  36. * @var WP_Block_Type
  37. */
  38. public $block_type;
  39. /**
  40. * Block context values.
  41. *
  42. * @since 5.5.0
  43. * @var array
  44. */
  45. public $context = array();
  46. /**
  47. * All available context of the current hierarchy.
  48. *
  49. * @since 5.5.0
  50. * @var array
  51. * @access protected
  52. */
  53. protected $available_context;
  54. /**
  55. * Block type registry.
  56. *
  57. * @since 5.9.0
  58. * @var WP_Block_Type_Registry
  59. * @access protected
  60. */
  61. protected $registry;
  62. /**
  63. * List of inner blocks (of this same class)
  64. *
  65. * @since 5.5.0
  66. * @var WP_Block_List
  67. */
  68. public $inner_blocks = array();
  69. /**
  70. * Resultant HTML from inside block comment delimiters after removing inner
  71. * blocks.
  72. *
  73. * @example "...Just <!-- wp:test /--> testing..." -> "Just testing..."
  74. *
  75. * @since 5.5.0
  76. * @var string
  77. */
  78. public $inner_html = '';
  79. /**
  80. * List of string fragments and null markers where inner blocks were found
  81. *
  82. * @example array(
  83. * 'inner_html' => 'BeforeInnerAfter',
  84. * 'inner_blocks' => array( block, block ),
  85. * 'inner_content' => array( 'Before', null, 'Inner', null, 'After' ),
  86. * )
  87. *
  88. * @since 5.5.0
  89. * @var array
  90. */
  91. public $inner_content = array();
  92. /**
  93. * Constructor.
  94. *
  95. * Populates object properties from the provided block instance argument.
  96. *
  97. * The given array of context values will not necessarily be available on
  98. * the instance itself, but is treated as the full set of values provided by
  99. * the block's ancestry. This is assigned to the private `available_context`
  100. * property. Only values which are configured to consumed by the block via
  101. * its registered type will be assigned to the block's `context` property.
  102. *
  103. * @since 5.5.0
  104. *
  105. * @param array $block Array of parsed block properties.
  106. * @param array $available_context Optional array of ancestry context values.
  107. * @param WP_Block_Type_Registry $registry Optional block type registry.
  108. */
  109. public function __construct( $block, $available_context = array(), $registry = null ) {
  110. $this->parsed_block = $block;
  111. $this->name = $block['blockName'];
  112. if ( is_null( $registry ) ) {
  113. $registry = WP_Block_Type_Registry::get_instance();
  114. }
  115. $this->registry = $registry;
  116. $this->block_type = $registry->get_registered( $this->name );
  117. $this->available_context = $available_context;
  118. if ( ! empty( $this->block_type->uses_context ) ) {
  119. foreach ( $this->block_type->uses_context as $context_name ) {
  120. if ( array_key_exists( $context_name, $this->available_context ) ) {
  121. $this->context[ $context_name ] = $this->available_context[ $context_name ];
  122. }
  123. }
  124. }
  125. if ( ! empty( $block['innerBlocks'] ) ) {
  126. $child_context = $this->available_context;
  127. if ( ! empty( $this->block_type->provides_context ) ) {
  128. foreach ( $this->block_type->provides_context as $context_name => $attribute_name ) {
  129. if ( array_key_exists( $attribute_name, $this->attributes ) ) {
  130. $child_context[ $context_name ] = $this->attributes[ $attribute_name ];
  131. }
  132. }
  133. }
  134. $this->inner_blocks = new WP_Block_List( $block['innerBlocks'], $child_context, $registry );
  135. }
  136. if ( ! empty( $block['innerHTML'] ) ) {
  137. $this->inner_html = $block['innerHTML'];
  138. }
  139. if ( ! empty( $block['innerContent'] ) ) {
  140. $this->inner_content = $block['innerContent'];
  141. }
  142. }
  143. /**
  144. * Returns a value from an inaccessible property.
  145. *
  146. * This is used to lazily initialize the `attributes` property of a block,
  147. * such that it is only prepared with default attributes at the time that
  148. * the property is accessed. For all other inaccessible properties, a `null`
  149. * value is returned.
  150. *
  151. * @since 5.5.0
  152. *
  153. * @param string $name Property name.
  154. * @return array|null Prepared attributes, or null.
  155. */
  156. public function __get( $name ) {
  157. if ( 'attributes' === $name ) {
  158. $this->attributes = isset( $this->parsed_block['attrs'] ) ?
  159. $this->parsed_block['attrs'] :
  160. array();
  161. if ( ! is_null( $this->block_type ) ) {
  162. $this->attributes = $this->block_type->prepare_attributes_for_render( $this->attributes );
  163. }
  164. return $this->attributes;
  165. }
  166. return null;
  167. }
  168. /**
  169. * Generates the render output for the block.
  170. *
  171. * @since 5.5.0
  172. *
  173. * @param array $options {
  174. * Optional options object.
  175. *
  176. * @type bool $dynamic Defaults to 'true'. Optionally set to false to avoid using the block's render_callback.
  177. * }
  178. * @return string Rendered block output.
  179. */
  180. public function render( $options = array() ) {
  181. global $post;
  182. $options = wp_parse_args(
  183. $options,
  184. array(
  185. 'dynamic' => true,
  186. )
  187. );
  188. $is_dynamic = $options['dynamic'] && $this->name && null !== $this->block_type && $this->block_type->is_dynamic();
  189. $block_content = '';
  190. if ( ! $options['dynamic'] || empty( $this->block_type->skip_inner_blocks ) ) {
  191. $index = 0;
  192. foreach ( $this->inner_content as $chunk ) {
  193. if ( is_string( $chunk ) ) {
  194. $block_content .= $chunk;
  195. } else {
  196. $inner_block = $this->inner_blocks[ $index ];
  197. $parent_block = $this;
  198. /** This filter is documented in wp-includes/blocks.php */
  199. $pre_render = apply_filters( 'pre_render_block', null, $inner_block->parsed_block, $parent_block );
  200. if ( ! is_null( $pre_render ) ) {
  201. $block_content .= $pre_render;
  202. } else {
  203. $source_block = $inner_block->parsed_block;
  204. /** This filter is documented in wp-includes/blocks.php */
  205. $inner_block->parsed_block = apply_filters( 'render_block_data', $inner_block->parsed_block, $source_block, $parent_block );
  206. /** This filter is documented in wp-includes/blocks.php */
  207. $inner_block->context = apply_filters( 'render_block_context', $inner_block->context, $inner_block->parsed_block, $parent_block );
  208. $block_content .= $inner_block->render();
  209. }
  210. $index++;
  211. }
  212. }
  213. }
  214. if ( $is_dynamic ) {
  215. $global_post = $post;
  216. $parent = WP_Block_Supports::$block_to_render;
  217. WP_Block_Supports::$block_to_render = $this->parsed_block;
  218. $block_content = (string) call_user_func( $this->block_type->render_callback, $this->attributes, $block_content, $this );
  219. WP_Block_Supports::$block_to_render = $parent;
  220. $post = $global_post;
  221. }
  222. if ( ( ! empty( $this->block_type->script_handles ) ) ) {
  223. foreach ( $this->block_type->script_handles as $script_handle ) {
  224. wp_enqueue_script( $script_handle );
  225. }
  226. }
  227. if ( ! empty( $this->block_type->view_script_handles ) ) {
  228. foreach ( $this->block_type->view_script_handles as $view_script_handle ) {
  229. wp_enqueue_script( $view_script_handle );
  230. }
  231. }
  232. if ( ( ! empty( $this->block_type->style_handles ) ) ) {
  233. foreach ( $this->block_type->style_handles as $style_handle ) {
  234. wp_enqueue_style( $style_handle );
  235. }
  236. }
  237. /**
  238. * Filters the content of a single block.
  239. *
  240. * @since 5.0.0
  241. * @since 5.9.0 The `$instance` parameter was added.
  242. *
  243. * @param string $block_content The block content.
  244. * @param array $block The full block, including name and attributes.
  245. * @param WP_Block $instance The block instance.
  246. */
  247. $block_content = apply_filters( 'render_block', $block_content, $this->parsed_block, $this );
  248. /**
  249. * Filters the content of a single block.
  250. *
  251. * The dynamic portion of the hook name, `$name`, refers to
  252. * the block name, e.g. "core/paragraph".
  253. *
  254. * @since 5.7.0
  255. * @since 5.9.0 The `$instance` parameter was added.
  256. *
  257. * @param string $block_content The block content.
  258. * @param array $block The full block, including name and attributes.
  259. * @param WP_Block $instance The block instance.
  260. */
  261. $block_content = apply_filters( "render_block_{$this->name}", $block_content, $this->parsed_block, $this );
  262. return $block_content;
  263. }
  264. }