30 /** |
31 /** |
31 * Unregisters a block type. |
32 * Unregisters a block type. |
32 * |
33 * |
33 * @since 5.0.0 |
34 * @since 5.0.0 |
34 * |
35 * |
35 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a |
36 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively |
36 * complete WP_Block_Type instance. |
37 * a complete WP_Block_Type instance. |
37 * @return WP_Block_Type|false The unregistered block type on success, or false on failure. |
38 * @return WP_Block_Type|false The unregistered block type on success, or false on failure. |
38 */ |
39 */ |
39 function unregister_block_type( $name ) { |
40 function unregister_block_type( $name ) { |
40 return WP_Block_Type_Registry::get_instance()->unregister( $name ); |
41 return WP_Block_Type_Registry::get_instance()->unregister( $name ); |
|
42 } |
|
43 |
|
44 /** |
|
45 * Removes the block asset's path prefix if provided. |
|
46 * |
|
47 * @since 5.5.0 |
|
48 * |
|
49 * @param string $asset_handle_or_path Asset handle or prefixed path. |
|
50 * @return string Path without the prefix or the original value. |
|
51 */ |
|
52 function remove_block_asset_path_prefix( $asset_handle_or_path ) { |
|
53 $path_prefix = 'file:'; |
|
54 if ( 0 !== strpos( $asset_handle_or_path, $path_prefix ) ) { |
|
55 return $asset_handle_or_path; |
|
56 } |
|
57 return substr( |
|
58 $asset_handle_or_path, |
|
59 strlen( $path_prefix ) |
|
60 ); |
|
61 } |
|
62 |
|
63 /** |
|
64 * Generates the name for an asset based on the name of the block |
|
65 * and the field name provided. |
|
66 * |
|
67 * @since 5.5.0 |
|
68 * |
|
69 * @param string $block_name Name of the block. |
|
70 * @param string $field_name Name of the metadata field. |
|
71 * @return string Generated asset name for the block's field. |
|
72 */ |
|
73 function generate_block_asset_handle( $block_name, $field_name ) { |
|
74 $field_mappings = array( |
|
75 'editorScript' => 'editor-script', |
|
76 'script' => 'script', |
|
77 'editorStyle' => 'editor-style', |
|
78 'style' => 'style', |
|
79 ); |
|
80 return str_replace( '/', '-', $block_name ) . |
|
81 '-' . $field_mappings[ $field_name ]; |
|
82 } |
|
83 |
|
84 /** |
|
85 * Finds a script handle for the selected block metadata field. It detects |
|
86 * when a path to file was provided and finds a corresponding asset file |
|
87 * with details necessary to register the script under automatically |
|
88 * generated handle name. It returns unprocessed script handle otherwise. |
|
89 * |
|
90 * @since 5.5.0 |
|
91 * |
|
92 * @param array $metadata Block metadata. |
|
93 * @param string $field_name Field name to pick from metadata. |
|
94 * @return string|bool Script handle provided directly or created through |
|
95 * script's registration, or false on failure. |
|
96 */ |
|
97 function register_block_script_handle( $metadata, $field_name ) { |
|
98 if ( empty( $metadata[ $field_name ] ) ) { |
|
99 return false; |
|
100 } |
|
101 $script_handle = $metadata[ $field_name ]; |
|
102 $script_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); |
|
103 if ( $script_handle === $script_path ) { |
|
104 return $script_handle; |
|
105 } |
|
106 |
|
107 $script_handle = generate_block_asset_handle( $metadata['name'], $field_name ); |
|
108 $script_asset_path = realpath( |
|
109 dirname( $metadata['file'] ) . '/' . |
|
110 substr_replace( $script_path, '.asset.php', - strlen( '.js' ) ) |
|
111 ); |
|
112 if ( ! file_exists( $script_asset_path ) ) { |
|
113 $message = sprintf( |
|
114 /* translators: %1: field name. %2: block name */ |
|
115 __( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ), |
|
116 $field_name, |
|
117 $metadata['name'] |
|
118 ); |
|
119 _doing_it_wrong( __FUNCTION__, $message, '5.5.0' ); |
|
120 return false; |
|
121 } |
|
122 $script_asset = require $script_asset_path; |
|
123 $result = wp_register_script( |
|
124 $script_handle, |
|
125 plugins_url( $script_path, $metadata['file'] ), |
|
126 $script_asset['dependencies'], |
|
127 $script_asset['version'] |
|
128 ); |
|
129 return $result ? $script_handle : false; |
|
130 } |
|
131 |
|
132 /** |
|
133 * Finds a style handle for the block metadata field. It detects when a path |
|
134 * to file was provided and registers the style under automatically |
|
135 * generated handle name. It returns unprocessed style handle otherwise. |
|
136 * |
|
137 * @since 5.5.0 |
|
138 * |
|
139 * @param array $metadata Block metadata. |
|
140 * @param string $field_name Field name to pick from metadata. |
|
141 * @return string|boolean Style handle provided directly or created through |
|
142 * style's registration, or false on failure. |
|
143 */ |
|
144 function register_block_style_handle( $metadata, $field_name ) { |
|
145 if ( empty( $metadata[ $field_name ] ) ) { |
|
146 return false; |
|
147 } |
|
148 $style_handle = $metadata[ $field_name ]; |
|
149 $style_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); |
|
150 if ( $style_handle === $style_path ) { |
|
151 return $style_handle; |
|
152 } |
|
153 |
|
154 $style_handle = generate_block_asset_handle( $metadata['name'], $field_name ); |
|
155 $block_dir = dirname( $metadata['file'] ); |
|
156 $result = wp_register_style( |
|
157 $style_handle, |
|
158 plugins_url( $style_path, $metadata['file'] ), |
|
159 array(), |
|
160 filemtime( realpath( "$block_dir/$style_path" ) ) |
|
161 ); |
|
162 return $result ? $style_handle : false; |
|
163 } |
|
164 |
|
165 /** |
|
166 * Registers a block type from metadata stored in the `block.json` file. |
|
167 * |
|
168 * @since 5.5.0 |
|
169 * |
|
170 * @param string $file_or_folder Path to the JSON file with metadata definition for |
|
171 * the block or path to the folder where the `block.json` file is located. |
|
172 * @param array $args { |
|
173 * Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`. |
|
174 * Any arguments may be defined, however the ones described below are supported by default. |
|
175 * Default empty array. |
|
176 * |
|
177 * @type callable $render_callback Callback used to render blocks of this block type. |
|
178 * } |
|
179 * @return WP_Block_Type|false The registered block type on success, or false on failure. |
|
180 */ |
|
181 function register_block_type_from_metadata( $file_or_folder, $args = array() ) { |
|
182 $filename = 'block.json'; |
|
183 $metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ? |
|
184 trailingslashit( $file_or_folder ) . $filename : |
|
185 $file_or_folder; |
|
186 if ( ! file_exists( $metadata_file ) ) { |
|
187 return false; |
|
188 } |
|
189 |
|
190 $metadata = json_decode( file_get_contents( $metadata_file ), true ); |
|
191 if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) { |
|
192 return false; |
|
193 } |
|
194 $metadata['file'] = $metadata_file; |
|
195 |
|
196 $settings = array(); |
|
197 $property_mappings = array( |
|
198 'title' => 'title', |
|
199 'category' => 'category', |
|
200 'parent' => 'parent', |
|
201 'icon' => 'icon', |
|
202 'description' => 'description', |
|
203 'keywords' => 'keywords', |
|
204 'attributes' => 'attributes', |
|
205 'providesContext' => 'provides_context', |
|
206 'usesContext' => 'uses_context', |
|
207 'supports' => 'supports', |
|
208 'styles' => 'styles', |
|
209 'example' => 'example', |
|
210 ); |
|
211 |
|
212 foreach ( $property_mappings as $key => $mapped_key ) { |
|
213 if ( isset( $metadata[ $key ] ) ) { |
|
214 $settings[ $mapped_key ] = $metadata[ $key ]; |
|
215 } |
|
216 } |
|
217 |
|
218 if ( ! empty( $metadata['editorScript'] ) ) { |
|
219 $settings['editor_script'] = register_block_script_handle( |
|
220 $metadata, |
|
221 'editorScript' |
|
222 ); |
|
223 } |
|
224 |
|
225 if ( ! empty( $metadata['script'] ) ) { |
|
226 $settings['script'] = register_block_script_handle( |
|
227 $metadata, |
|
228 'script' |
|
229 ); |
|
230 } |
|
231 |
|
232 if ( ! empty( $metadata['editorStyle'] ) ) { |
|
233 $settings['editor_style'] = register_block_style_handle( |
|
234 $metadata, |
|
235 'editorStyle' |
|
236 ); |
|
237 } |
|
238 |
|
239 if ( ! empty( $metadata['style'] ) ) { |
|
240 $settings['style'] = register_block_style_handle( |
|
241 $metadata, |
|
242 'style' |
|
243 ); |
|
244 } |
|
245 |
|
246 return register_block_type( |
|
247 $metadata['name'], |
|
248 array_merge( |
|
249 $settings, |
|
250 $args |
|
251 ) |
|
252 ); |
41 } |
253 } |
42 |
254 |
43 /** |
255 /** |
44 * Determine whether a post or content string has blocks. |
256 * Determine whether a post or content string has blocks. |
45 * |
257 * |
46 * This test optimizes for performance rather than strict accuracy, detecting |
258 * This test optimizes for performance rather than strict accuracy, detecting |
47 * the pattern of a block but not validating its structure. For strict accuracy, |
259 * the pattern of a block but not validating its structure. For strict accuracy, |
48 * you should use the block parser on post content. |
260 * you should use the block parser on post content. |
49 * |
261 * |
50 * @since 5.0.0 |
262 * @since 5.0.0 |
|
263 * |
51 * @see parse_blocks() |
264 * @see parse_blocks() |
52 * |
265 * |
53 * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. |
266 * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. |
54 * @return bool Whether the post has blocks. |
267 * @return bool Whether the post has blocks. |
55 */ |
268 */ |
109 $dynamic_block_names[] = $block_type->name; |
346 $dynamic_block_names[] = $block_type->name; |
110 } |
347 } |
111 } |
348 } |
112 |
349 |
113 return $dynamic_block_names; |
350 return $dynamic_block_names; |
|
351 } |
|
352 |
|
353 /** |
|
354 * Given an array of attributes, returns a string in the serialized attributes |
|
355 * format prepared for post content. |
|
356 * |
|
357 * The serialized result is a JSON-encoded string, with unicode escape sequence |
|
358 * substitution for characters which might otherwise interfere with embedding |
|
359 * the result in an HTML comment. |
|
360 * |
|
361 * @since 5.3.1 |
|
362 * |
|
363 * @param array $block_attributes Attributes object. |
|
364 * @return string Serialized attributes. |
|
365 */ |
|
366 function serialize_block_attributes( $block_attributes ) { |
|
367 $encoded_attributes = json_encode( $block_attributes ); |
|
368 $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes ); |
|
369 $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes ); |
|
370 $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes ); |
|
371 $encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes ); |
|
372 // Regex: /\\"/ |
|
373 $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes ); |
|
374 |
|
375 return $encoded_attributes; |
|
376 } |
|
377 |
|
378 /** |
|
379 * Returns the block name to use for serialization. This will remove the default |
|
380 * "core/" namespace from a block name. |
|
381 * |
|
382 * @since 5.3.1 |
|
383 * |
|
384 * @param string $block_name Original block name. |
|
385 * @return string Block name to use for serialization. |
|
386 */ |
|
387 function strip_core_block_namespace( $block_name = null ) { |
|
388 if ( is_string( $block_name ) && 0 === strpos( $block_name, 'core/' ) ) { |
|
389 return substr( $block_name, 5 ); |
|
390 } |
|
391 |
|
392 return $block_name; |
|
393 } |
|
394 |
|
395 /** |
|
396 * Returns the content of a block, including comment delimiters. |
|
397 * |
|
398 * @since 5.3.1 |
|
399 * |
|
400 * @param string $block_name Block name. |
|
401 * @param array $block_attributes Block attributes. |
|
402 * @param string $block_content Block save content. |
|
403 * @return string Comment-delimited block content. |
|
404 */ |
|
405 function get_comment_delimited_block_content( $block_name = null, $block_attributes, $block_content ) { |
|
406 if ( is_null( $block_name ) ) { |
|
407 return $block_content; |
|
408 } |
|
409 |
|
410 $serialized_block_name = strip_core_block_namespace( $block_name ); |
|
411 $serialized_attributes = empty( $block_attributes ) ? '' : serialize_block_attributes( $block_attributes ) . ' '; |
|
412 |
|
413 if ( empty( $block_content ) ) { |
|
414 return sprintf( '<!-- wp:%s %s/-->', $serialized_block_name, $serialized_attributes ); |
|
415 } |
|
416 |
|
417 return sprintf( |
|
418 '<!-- wp:%s %s-->%s<!-- /wp:%s -->', |
|
419 $serialized_block_name, |
|
420 $serialized_attributes, |
|
421 $block_content, |
|
422 $serialized_block_name |
|
423 ); |
|
424 } |
|
425 |
|
426 /** |
|
427 * Returns the content of a block, including comment delimiters, serializing all |
|
428 * attributes from the given parsed block. |
|
429 * |
|
430 * This should be used when preparing a block to be saved to post content. |
|
431 * Prefer `render_block` when preparing a block for display. Unlike |
|
432 * `render_block`, this does not evaluate a block's `render_callback`, and will |
|
433 * instead preserve the markup as parsed. |
|
434 * |
|
435 * @since 5.3.1 |
|
436 * |
|
437 * @param WP_Block_Parser_Block $block A single parsed block object. |
|
438 * @return string String of rendered HTML. |
|
439 */ |
|
440 function serialize_block( $block ) { |
|
441 $block_content = ''; |
|
442 |
|
443 $index = 0; |
|
444 foreach ( $block['innerContent'] as $chunk ) { |
|
445 $block_content .= is_string( $chunk ) ? $chunk : serialize_block( $block['innerBlocks'][ $index++ ] ); |
|
446 } |
|
447 |
|
448 if ( ! is_array( $block['attrs'] ) ) { |
|
449 $block['attrs'] = array(); |
|
450 } |
|
451 |
|
452 return get_comment_delimited_block_content( |
|
453 $block['blockName'], |
|
454 $block['attrs'], |
|
455 $block_content |
|
456 ); |
|
457 } |
|
458 |
|
459 /** |
|
460 * Returns a joined string of the aggregate serialization of the given parsed |
|
461 * blocks. |
|
462 * |
|
463 * @since 5.3.1 |
|
464 * |
|
465 * @param WP_Block_Parser_Block[] $blocks Parsed block objects. |
|
466 * @return string String of rendered HTML. |
|
467 */ |
|
468 function serialize_blocks( $blocks ) { |
|
469 return implode( '', array_map( 'serialize_block', $blocks ) ); |
|
470 } |
|
471 |
|
472 /** |
|
473 * Filters and sanitizes block content to remove non-allowable HTML from |
|
474 * parsed block attribute values. |
|
475 * |
|
476 * @since 5.3.1 |
|
477 * |
|
478 * @param string $text Text that may contain block content. |
|
479 * @param array[]|string $allowed_html An array of allowed HTML elements |
|
480 * and attributes, or a context name |
|
481 * such as 'post'. |
|
482 * @param string[] $allowed_protocols Array of allowed URL protocols. |
|
483 * @return string The filtered and sanitized content result. |
|
484 */ |
|
485 function filter_block_content( $text, $allowed_html = 'post', $allowed_protocols = array() ) { |
|
486 $result = ''; |
|
487 |
|
488 $blocks = parse_blocks( $text ); |
|
489 foreach ( $blocks as $block ) { |
|
490 $block = filter_block_kses( $block, $allowed_html, $allowed_protocols ); |
|
491 $result .= serialize_block( $block ); |
|
492 } |
|
493 |
|
494 return $result; |
|
495 } |
|
496 |
|
497 /** |
|
498 * Filters and sanitizes a parsed block to remove non-allowable HTML from block |
|
499 * attribute values. |
|
500 * |
|
501 * @since 5.3.1 |
|
502 * |
|
503 * @param WP_Block_Parser_Block $block The parsed block object. |
|
504 * @param array[]|string $allowed_html An array of allowed HTML |
|
505 * elements and attributes, or a |
|
506 * context name such as 'post'. |
|
507 * @param string[] $allowed_protocols Allowed URL protocols. |
|
508 * @return array The filtered and sanitized block object result. |
|
509 */ |
|
510 function filter_block_kses( $block, $allowed_html, $allowed_protocols = array() ) { |
|
511 $block['attrs'] = filter_block_kses_value( $block['attrs'], $allowed_html, $allowed_protocols ); |
|
512 |
|
513 if ( is_array( $block['innerBlocks'] ) ) { |
|
514 foreach ( $block['innerBlocks'] as $i => $inner_block ) { |
|
515 $block['innerBlocks'][ $i ] = filter_block_kses( $inner_block, $allowed_html, $allowed_protocols ); |
|
516 } |
|
517 } |
|
518 |
|
519 return $block; |
|
520 } |
|
521 |
|
522 /** |
|
523 * Filters and sanitizes a parsed block attribute value to remove non-allowable |
|
524 * HTML. |
|
525 * |
|
526 * @since 5.3.1 |
|
527 * |
|
528 * @param string[]|string $value The attribute value to filter. |
|
529 * @param array[]|string $allowed_html An array of allowed HTML elements |
|
530 * and attributes, or a context name |
|
531 * such as 'post'. |
|
532 * @param string[] $allowed_protocols Array of allowed URL protocols. |
|
533 * @return string[]|string The filtered and sanitized result. |
|
534 */ |
|
535 function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = array() ) { |
|
536 if ( is_array( $value ) ) { |
|
537 foreach ( $value as $key => $inner_value ) { |
|
538 $filtered_key = filter_block_kses_value( $key, $allowed_html, $allowed_protocols ); |
|
539 $filtered_value = filter_block_kses_value( $inner_value, $allowed_html, $allowed_protocols ); |
|
540 |
|
541 if ( $filtered_key !== $key ) { |
|
542 unset( $value[ $key ] ); |
|
543 } |
|
544 |
|
545 $value[ $filtered_key ] = $filtered_value; |
|
546 } |
|
547 } elseif ( is_string( $value ) ) { |
|
548 return wp_kses( $value, $allowed_html, $allowed_protocols ); |
|
549 } |
|
550 |
|
551 return $value; |
114 } |
552 } |
115 |
553 |
116 /** |
554 /** |
117 * Parses blocks out of a content string, and renders those appropriate for the excerpt. |
555 * Parses blocks out of a content string, and renders those appropriate for the excerpt. |
118 * |
556 * |
210 /** |
648 /** |
211 * Renders a single block into a HTML string. |
649 * Renders a single block into a HTML string. |
212 * |
650 * |
213 * @since 5.0.0 |
651 * @since 5.0.0 |
214 * |
652 * |
215 * @global WP_Post $post The post to edit. |
653 * @global WP_Post $post The post to edit. |
216 * |
654 * @global WP_Query $wp_query WordPress Query object. |
217 * @param array $block A single parsed block object. |
655 * |
|
656 * @param array $parsed_block A single parsed block object. |
218 * @return string String of rendered HTML. |
657 * @return string String of rendered HTML. |
219 */ |
658 */ |
220 function render_block( $block ) { |
659 function render_block( $parsed_block ) { |
221 global $post; |
660 global $post, $wp_query; |
222 |
661 |
223 /** |
662 /** |
224 * Allows render_block() to be shortcircuited, by returning a non-null value. |
663 * Allows render_block() to be short-circuited, by returning a non-null value. |
225 * |
664 * |
226 * @since 5.1.0 |
665 * @since 5.1.0 |
227 * |
666 * |
228 * @param string $pre_render The pre-rendered content. Default null. |
667 * @param string|null $pre_render The pre-rendered content. Default null. |
229 * @param array $block The block being rendered. |
668 * @param array $parsed_block The block being rendered. |
230 */ |
669 */ |
231 $pre_render = apply_filters( 'pre_render_block', null, $block ); |
670 $pre_render = apply_filters( 'pre_render_block', null, $parsed_block ); |
232 if ( ! is_null( $pre_render ) ) { |
671 if ( ! is_null( $pre_render ) ) { |
233 return $pre_render; |
672 return $pre_render; |
234 } |
673 } |
235 |
674 |
236 $source_block = $block; |
675 $source_block = $parsed_block; |
237 |
676 |
238 /** |
677 /** |
239 * Filters the block being rendered in render_block(), before it's processed. |
678 * Filters the block being rendered in render_block(), before it's processed. |
240 * |
679 * |
241 * @since 5.1.0 |
680 * @since 5.1.0 |
242 * |
681 * |
243 * @param array $block The block being rendered. |
682 * @param array $parsed_block The block being rendered. |
244 * @param array $source_block An un-modified copy of $block, as it appeared in the source content. |
683 * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content. |
245 */ |
684 */ |
246 $block = apply_filters( 'render_block_data', $block, $source_block ); |
685 $parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block ); |
247 |
686 |
248 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); |
687 $context = array(); |
249 $is_dynamic = $block['blockName'] && null !== $block_type && $block_type->is_dynamic(); |
688 |
250 $block_content = ''; |
689 if ( $post instanceof WP_Post ) { |
251 $index = 0; |
690 $context['postId'] = $post->ID; |
252 |
691 |
253 foreach ( $block['innerContent'] as $chunk ) { |
692 /* |
254 $block_content .= is_string( $chunk ) ? $chunk : render_block( $block['innerBlocks'][ $index++ ] ); |
693 * The `postType` context is largely unnecessary server-side, since the ID |
255 } |
694 * is usually sufficient on its own. That being said, since a block's |
256 |
695 * manifest is expected to be shared between the server and the client, |
257 if ( ! is_array( $block['attrs'] ) ) { |
696 * it should be included to consistently fulfill the expectation. |
258 $block['attrs'] = array(); |
697 */ |
259 } |
698 $context['postType'] = $post->post_type; |
260 |
699 } |
261 if ( $is_dynamic ) { |
700 |
262 $global_post = $post; |
701 if ( $wp_query instanceof WP_Query && isset( $wp_query->tax_query->queried_terms['category'] ) ) { |
263 $block_content = $block_type->render( $block['attrs'], $block_content ); |
702 $context['query'] = array( 'categoryIds' => array() ); |
264 $post = $global_post; |
703 foreach ( $wp_query->tax_query->queried_terms['category']['terms'] as $category_slug_or_id ) { |
|
704 $context['query']['categoryIds'][] = 'slug' === $wp_query->tax_query->queried_terms['category']['field'] ? get_cat_ID( $category_slug_or_id ) : $category_slug_or_id; |
|
705 } |
265 } |
706 } |
266 |
707 |
267 /** |
708 /** |
268 * Filters the content of a single block. |
709 * Filters the default context provided to a rendered block. |
269 * |
710 * |
270 * @since 5.0.0 |
711 * @since 5.5.0 |
271 * |
712 * |
272 * @param string $block_content The block content about to be appended. |
713 * @param array $context Default context. |
273 * @param array $block The full block, including name and attributes. |
714 * @param array $parsed_block Block being rendered, filtered by `render_block_data`. |
274 */ |
715 */ |
275 return apply_filters( 'render_block', $block_content, $block ); |
716 $context = apply_filters( 'render_block_context', $context, $parsed_block ); |
|
717 |
|
718 $block = new WP_Block( $parsed_block, $context ); |
|
719 |
|
720 return $block->render(); |
276 } |
721 } |
277 |
722 |
278 /** |
723 /** |
279 * Parses blocks out of a content string. |
724 * Parses blocks out of a content string. |
280 * |
725 * |
281 * @since 5.0.0 |
726 * @since 5.0.0 |
282 * |
727 * |
283 * @param string $content Post content. |
728 * @param string $content Post content. |
284 * @return array Array of parsed block objects. |
729 * @return array[] Array of parsed block objects. |
285 */ |
730 */ |
286 function parse_blocks( $content ) { |
731 function parse_blocks( $content ) { |
287 /** |
732 /** |
288 * Filter to allow plugins to replace the server-side block parser |
733 * Filter to allow plugins to replace the server-side block parser |
289 * |
734 * |