Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-block.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* Blocks API: WP_Block class
4
+
*
5
+
* @package WordPress
6
+
* @since 5.5.0
7
+
*/
8
+
9
+
/**
10
+
* Class representing a parsed instance of a block.
11
+
*
12
+
* @since 5.5.0
13
+
* @property array $attributes
14
+
*/
15
+
#[AllowDynamicProperties]
16
+
class WP_Block {
17
+
18
+
/**
19
+
* Original parsed array representation of block.
20
+
*
21
+
* @since 5.5.0
22
+
* @var array
23
+
*/
24
+
public $parsed_block;
25
+
26
+
/**
27
+
* Name of block.
28
+
*
29
+
* @example "core/paragraph"
30
+
*
31
+
* @since 5.5.0
32
+
* @var string|null
33
+
*/
34
+
public $name;
35
+
36
+
/**
37
+
* Block type associated with the instance.
38
+
*
39
+
* @since 5.5.0
40
+
* @var WP_Block_Type
41
+
*/
42
+
public $block_type;
43
+
44
+
/**
45
+
* Block context values.
46
+
*
47
+
* @since 5.5.0
48
+
* @var array
49
+
*/
50
+
public $context = array();
51
+
52
+
/**
53
+
* All available context of the current hierarchy.
54
+
*
55
+
* @since 5.5.0
56
+
* @var array
57
+
*/
58
+
protected $available_context = array();
59
+
60
+
/**
61
+
* Block type registry.
62
+
*
63
+
* @since 5.9.0
64
+
* @var WP_Block_Type_Registry
65
+
*/
66
+
protected $registry;
67
+
68
+
/**
69
+
* List of inner blocks (of this same class)
70
+
*
71
+
* @since 5.5.0
72
+
* @var WP_Block_List
73
+
*/
74
+
public $inner_blocks = array();
75
+
76
+
/**
77
+
* Resultant HTML from inside block comment delimiters after removing inner
78
+
* blocks.
79
+
*
80
+
* @example "...Just <!-- wp:test /--> testing..." -> "Just testing..."
81
+
*
82
+
* @since 5.5.0
83
+
* @var string
84
+
*/
85
+
public $inner_html = '';
86
+
87
+
/**
88
+
* List of string fragments and null markers where inner blocks were found
89
+
*
90
+
* @example array(
91
+
* 'inner_html' => 'BeforeInnerAfter',
92
+
* 'inner_blocks' => array( block, block ),
93
+
* 'inner_content' => array( 'Before', null, 'Inner', null, 'After' ),
94
+
* )
95
+
*
96
+
* @since 5.5.0
97
+
* @var array
98
+
*/
99
+
public $inner_content = array();
100
+
101
+
/**
102
+
* Constructor.
103
+
*
104
+
* Populates object properties from the provided block instance argument.
105
+
*
106
+
* The given array of context values will not necessarily be available on
107
+
* the instance itself, but is treated as the full set of values provided by
108
+
* the block's ancestry. This is assigned to the private `available_context`
109
+
* property. Only values which are configured to consumed by the block via
110
+
* its registered type will be assigned to the block's `context` property.
111
+
*
112
+
* @since 5.5.0
113
+
*
114
+
* @param array $block {
115
+
* An associative array of a single parsed block object. See WP_Block_Parser_Block.
116
+
*
117
+
* @type string|null $blockName Name of block.
118
+
* @type array $attrs Attributes from block comment delimiters.
119
+
* @type array $innerBlocks List of inner blocks. An array of arrays that
120
+
* have the same structure as this one.
121
+
* @type string $innerHTML HTML from inside block comment delimiters.
122
+
* @type array $innerContent List of string fragments and null markers where inner blocks were found.
123
+
* }
124
+
* @param array $available_context Optional array of ancestry context values.
125
+
* @param WP_Block_Type_Registry $registry Optional block type registry.
126
+
*/
127
+
public function __construct( $block, $available_context = array(), $registry = null ) {
128
+
$this->parsed_block = $block;
129
+
$this->name = $block['blockName'];
130
+
131
+
if ( is_null( $registry ) ) {
132
+
$registry = WP_Block_Type_Registry::get_instance();
133
+
}
134
+
135
+
$this->registry = $registry;
136
+
137
+
$this->block_type = $registry->get_registered( $this->name );
138
+
139
+
$this->available_context = $available_context;
140
+
141
+
$this->refresh_context_dependents();
142
+
}
143
+
144
+
/**
145
+
* Updates the context for the current block and its inner blocks.
146
+
*
147
+
* The method updates the context of inner blocks, if any, by passing down
148
+
* any context values the block provides (`provides_context`).
149
+
*
150
+
* If the block has inner blocks, the method recursively processes them by creating new instances of `WP_Block`
151
+
* for each inner block and updating their context based on the block's `provides_context` property.
152
+
*
153
+
* @since 6.8.0
154
+
*/
155
+
public function refresh_context_dependents() {
156
+
/*
157
+
* Merging the `$context` property here is not ideal, but for now needs to happen because of backward compatibility.
158
+
* Ideally, the `$context` property itself would not be filterable directly and only the `$available_context` would be filterable.
159
+
* However, this needs to be separately explored whether it's possible without breakage.
160
+
*/
161
+
$this->available_context = array_merge( $this->available_context, $this->context );
162
+
163
+
if ( ! empty( $this->block_type->uses_context ) ) {
164
+
foreach ( $this->block_type->uses_context as $context_name ) {
165
+
if ( array_key_exists( $context_name, $this->available_context ) ) {
166
+
$this->context[ $context_name ] = $this->available_context[ $context_name ];
167
+
}
168
+
}
169
+
}
170
+
171
+
$this->refresh_parsed_block_dependents();
172
+
}
173
+
174
+
/**
175
+
* Updates the parsed block content for the current block and its inner blocks.
176
+
*
177
+
* This method sets the `inner_html` and `inner_content` properties of the block based on the parsed
178
+
* block content provided during initialization. It ensures that the block instance reflects the
179
+
* most up-to-date content for both the inner HTML and any string fragments around inner blocks.
180
+
*
181
+
* If the block has inner blocks, this method initializes a new `WP_Block_List` for them, ensuring the
182
+
* correct content and context are updated for each nested block.
183
+
*
184
+
* @since 6.8.0
185
+
*/
186
+
public function refresh_parsed_block_dependents() {
187
+
if ( ! empty( $this->parsed_block['innerBlocks'] ) ) {
188
+
$child_context = $this->available_context;
189
+
190
+
if ( ! empty( $this->block_type->provides_context ) ) {
191
+
foreach ( $this->block_type->provides_context as $context_name => $attribute_name ) {
192
+
if ( array_key_exists( $attribute_name, $this->attributes ) ) {
193
+
$child_context[ $context_name ] = $this->attributes[ $attribute_name ];
194
+
}
195
+
}
196
+
}
197
+
198
+
$this->inner_blocks = new WP_Block_List( $this->parsed_block['innerBlocks'], $child_context, $this->registry );
199
+
}
200
+
201
+
if ( ! empty( $this->parsed_block['innerHTML'] ) ) {
202
+
$this->inner_html = $this->parsed_block['innerHTML'];
203
+
}
204
+
205
+
if ( ! empty( $this->parsed_block['innerContent'] ) ) {
206
+
$this->inner_content = $this->parsed_block['innerContent'];
207
+
}
208
+
}
209
+
210
+
/**
211
+
* Returns a value from an inaccessible property.
212
+
*
213
+
* This is used to lazily initialize the `attributes` property of a block,
214
+
* such that it is only prepared with default attributes at the time that
215
+
* the property is accessed. For all other inaccessible properties, a `null`
216
+
* value is returned.
217
+
*
218
+
* @since 5.5.0
219
+
*
220
+
* @param string $name Property name.
221
+
* @return array|null Prepared attributes, or null.
222
+
*/
223
+
public function __get( $name ) {
224
+
if ( 'attributes' === $name ) {
225
+
$this->attributes = isset( $this->parsed_block['attrs'] ) ?
226
+
$this->parsed_block['attrs'] :
227
+
array();
228
+
229
+
if ( ! is_null( $this->block_type ) ) {
230
+
$this->attributes = $this->block_type->prepare_attributes_for_render( $this->attributes );
231
+
}
232
+
233
+
return $this->attributes;
234
+
}
235
+
236
+
return null;
237
+
}
238
+
239
+
/**
240
+
* Processes the block bindings and updates the block attributes with the values from the sources.
241
+
*
242
+
* A block might contain bindings in its attributes. Bindings are mappings
243
+
* between an attribute of the block and a source. A "source" is a function
244
+
* registered with `register_block_bindings_source()` that defines how to
245
+
* retrieve a value from outside the block, e.g. from post meta.
246
+
*
247
+
* This function will process those bindings and update the block's attributes
248
+
* with the values coming from the bindings.
249
+
*
250
+
* ### Example
251
+
*
252
+
* The "bindings" property for an Image block might look like this:
253
+
*
254
+
* ```json
255
+
* {
256
+
* "metadata": {
257
+
* "bindings": {
258
+
* "title": {
259
+
* "source": "core/post-meta",
260
+
* "args": { "key": "text_custom_field" }
261
+
* },
262
+
* "url": {
263
+
* "source": "core/post-meta",
264
+
* "args": { "key": "url_custom_field" }
265
+
* }
266
+
* }
267
+
* }
268
+
* }
269
+
* ```
270
+
*
271
+
* The above example will replace the `title` and `url` attributes of the Image
272
+
* block with the values of the `text_custom_field` and `url_custom_field` post meta.
273
+
*
274
+
* @since 6.5.0
275
+
* @since 6.6.0 Handle the `__default` attribute for pattern overrides.
276
+
* @since 6.7.0 Return any updated bindings metadata in the computed attributes.
277
+
*
278
+
* @return array The computed block attributes for the provided block bindings.
279
+
*/
280
+
private function process_block_bindings() {
281
+
$block_type = $this->name;
282
+
$parsed_block = $this->parsed_block;
283
+
$computed_attributes = array();
284
+
$supported_block_attributes = get_block_bindings_supported_attributes( $block_type );
285
+
286
+
// If the block doesn't have the bindings property, isn't one of the supported
287
+
// block types, or the bindings property is not an array, return the block content.
288
+
if (
289
+
empty( $supported_block_attributes ) ||
290
+
empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
291
+
! is_array( $parsed_block['attrs']['metadata']['bindings'] )
292
+
) {
293
+
return $computed_attributes;
294
+
}
295
+
296
+
$bindings = $parsed_block['attrs']['metadata']['bindings'];
297
+
298
+
/*
299
+
* If the default binding is set for pattern overrides, replace it
300
+
* with a pattern override binding for all supported attributes.
301
+
*/
302
+
if (
303
+
isset( $bindings['__default']['source'] ) &&
304
+
'core/pattern-overrides' === $bindings['__default']['source']
305
+
) {
306
+
$updated_bindings = array();
307
+
308
+
/*
309
+
* Build a binding array of all supported attributes.
310
+
* Note that this also omits the `__default` attribute from the
311
+
* resulting array.
312
+
*/
313
+
foreach ( $supported_block_attributes as $attribute_name ) {
314
+
// Retain any non-pattern override bindings that might be present.
315
+
$updated_bindings[ $attribute_name ] = isset( $bindings[ $attribute_name ] )
316
+
? $bindings[ $attribute_name ]
317
+
: array( 'source' => 'core/pattern-overrides' );
318
+
}
319
+
$bindings = $updated_bindings;
320
+
/*
321
+
* Update the bindings metadata of the computed attributes.
322
+
* This ensures the block receives the expanded __default binding metadata when it renders.
323
+
*/
324
+
$computed_attributes['metadata'] = array_merge(
325
+
$parsed_block['attrs']['metadata'],
326
+
array( 'bindings' => $bindings )
327
+
);
328
+
}
329
+
330
+
foreach ( $bindings as $attribute_name => $block_binding ) {
331
+
// If the attribute is not in the supported list, process next attribute.
332
+
if ( ! in_array( $attribute_name, $supported_block_attributes, true ) ) {
333
+
continue;
334
+
}
335
+
// If no source is provided, or that source is not registered, process next attribute.
336
+
if ( ! isset( $block_binding['source'] ) || ! is_string( $block_binding['source'] ) ) {
337
+
continue;
338
+
}
339
+
340
+
$block_binding_source = get_block_bindings_source( $block_binding['source'] );
341
+
if ( null === $block_binding_source ) {
342
+
continue;
343
+
}
344
+
345
+
// Adds the necessary context defined by the source.
346
+
if ( ! empty( $block_binding_source->uses_context ) ) {
347
+
foreach ( $block_binding_source->uses_context as $context_name ) {
348
+
if ( array_key_exists( $context_name, $this->available_context ) ) {
349
+
$this->context[ $context_name ] = $this->available_context[ $context_name ];
350
+
}
351
+
}
352
+
}
353
+
354
+
$source_args = ! empty( $block_binding['args'] ) && is_array( $block_binding['args'] ) ? $block_binding['args'] : array();
355
+
$source_value = $block_binding_source->get_value( $source_args, $this, $attribute_name );
356
+
357
+
// If the value is not null, process the HTML based on the block and the attribute.
358
+
if ( ! is_null( $source_value ) ) {
359
+
$computed_attributes[ $attribute_name ] = $source_value;
360
+
}
361
+
}
362
+
363
+
return $computed_attributes;
364
+
}
365
+
366
+
/**
367
+
* Depending on the block attribute name, replace its value in the HTML based on the value provided.
368
+
*
369
+
* @since 6.5.0
370
+
*
371
+
* @param string $block_content Block content.
372
+
* @param string $attribute_name The attribute name to replace.
373
+
* @param mixed $source_value The value used to replace in the HTML.
374
+
* @return string The modified block content.
375
+
*/
376
+
private function replace_html( string $block_content, string $attribute_name, $source_value ) {
377
+
$block_type = $this->block_type;
378
+
if ( ! isset( $block_type->attributes[ $attribute_name ]['source'] ) ) {
379
+
return $block_content;
380
+
}
381
+
382
+
// Depending on the attribute source, the processing will be different.
383
+
switch ( $block_type->attributes[ $attribute_name ]['source'] ) {
384
+
case 'html':
385
+
case 'rich-text':
386
+
$block_reader = self::get_block_bindings_processor( $block_content );
387
+
388
+
// TODO: Support for CSS selectors whenever they are ready in the HTML API.
389
+
// In the meantime, support comma-separated selectors by exploding them into an array.
390
+
$selectors = explode( ',', $block_type->attributes[ $attribute_name ]['selector'] );
391
+
// Add a bookmark to the first tag to be able to iterate over the selectors.
392
+
$block_reader->next_tag();
393
+
$block_reader->set_bookmark( 'iterate-selectors' );
394
+
395
+
foreach ( $selectors as $selector ) {
396
+
// If the parent tag, or any of its children, matches the selector, replace the HTML.
397
+
if ( strcasecmp( $block_reader->get_tag(), $selector ) === 0 || $block_reader->next_tag(
398
+
array(
399
+
'tag_name' => $selector,
400
+
)
401
+
) ) {
402
+
// TODO: Use `WP_HTML_Processor::set_inner_html` method once it's available.
403
+
$block_reader->release_bookmark( 'iterate-selectors' );
404
+
$block_reader->replace_rich_text( wp_kses_post( $source_value ) );
405
+
return $block_reader->get_updated_html();
406
+
} else {
407
+
$block_reader->seek( 'iterate-selectors' );
408
+
}
409
+
}
410
+
$block_reader->release_bookmark( 'iterate-selectors' );
411
+
return $block_content;
412
+
413
+
case 'attribute':
414
+
$amended_content = new WP_HTML_Tag_Processor( $block_content );
415
+
if ( ! $amended_content->next_tag(
416
+
array(
417
+
// TODO: build the query from CSS selector.
418
+
'tag_name' => $block_type->attributes[ $attribute_name ]['selector'],
419
+
)
420
+
) ) {
421
+
return $block_content;
422
+
}
423
+
$amended_content->set_attribute( $block_type->attributes[ $attribute_name ]['attribute'], $source_value );
424
+
return $amended_content->get_updated_html();
425
+
426
+
default:
427
+
return $block_content;
428
+
}
429
+
}
430
+
431
+
private static function get_block_bindings_processor( string $block_content ) {
432
+
$internal_processor_class = new class('', WP_HTML_Processor::CONSTRUCTOR_UNLOCK_CODE) extends WP_HTML_Processor {
433
+
/**
434
+
* Replace the rich text content between a tag opener and matching closer.
435
+
*
436
+
* When stopped on a tag opener, replace the content enclosed by it and its
437
+
* matching closer with the provided rich text.
438
+
*
439
+
* @param string $rich_text The rich text to replace the original content with.
440
+
* @return bool True on success.
441
+
*/
442
+
public function replace_rich_text( $rich_text ) {
443
+
if ( $this->is_tag_closer() || ! $this->expects_closer() ) {
444
+
return false;
445
+
}
446
+
447
+
$depth = $this->get_current_depth();
448
+
$tag_name = $this->get_tag();
449
+
450
+
$this->set_bookmark( '_wp_block_bindings' );
451
+
// The bookmark names are prefixed with `_` so the key below has an extra `_`.
452
+
$tag_opener = $this->bookmarks['__wp_block_bindings'];
453
+
$start = $tag_opener->start + $tag_opener->length;
454
+
455
+
// Find matching tag closer.
456
+
while ( $this->next_token() && $this->get_current_depth() >= $depth ) {
457
+
}
458
+
459
+
if ( ! $this->is_tag_closer() || $tag_name !== $this->get_tag() ) {
460
+
return false;
461
+
}
462
+
463
+
$this->set_bookmark( '_wp_block_bindings' );
464
+
$tag_closer = $this->bookmarks['__wp_block_bindings'];
465
+
$end = $tag_closer->start;
466
+
467
+
$this->lexical_updates[] = new WP_HTML_Text_Replacement(
468
+
$start,
469
+
$end - $start,
470
+
$rich_text
471
+
);
472
+
473
+
return true;
474
+
}
475
+
};
476
+
477
+
return $internal_processor_class::create_fragment( $block_content );
478
+
}
479
+
480
+
/**
481
+
* Generates the render output for the block.
482
+
*
483
+
* @since 5.5.0
484
+
* @since 6.5.0 Added block bindings processing.
485
+
*
486
+
* @global WP_Post $post Global post object.
487
+
*
488
+
* @param array $options {
489
+
* Optional options object.
490
+
*
491
+
* @type bool $dynamic Defaults to 'true'. Optionally set to false to avoid using the block's render_callback.
492
+
* }
493
+
* @return string Rendered block output.
494
+
*/
495
+
public function render( $options = array() ) {
496
+
global $post;
497
+
498
+
$before_wp_enqueue_scripts_count = did_action( 'wp_enqueue_scripts' );
499
+
500
+
// Capture the current assets queues.
501
+
$before_styles_queue = wp_styles()->queue;
502
+
$before_scripts_queue = wp_scripts()->queue;
503
+
$before_script_modules_queue = wp_script_modules()->get_queue();
504
+
505
+
/*
506
+
* There can be only one root interactive block at a time because the rendered HTML of that block contains
507
+
* the rendered HTML of all its inner blocks, including any interactive block.
508
+
*/
509
+
static $root_interactive_block = null;
510
+
/**
511
+
* Filters whether Interactivity API should process directives.
512
+
*
513
+
* @since 6.6.0
514
+
*
515
+
* @param bool $enabled Whether the directives processing is enabled.
516
+
*/
517
+
$interactivity_process_directives_enabled = apply_filters( 'interactivity_process_directives', true );
518
+
if (
519
+
$interactivity_process_directives_enabled && null === $root_interactive_block && (
520
+
( isset( $this->block_type->supports['interactivity'] ) && true === $this->block_type->supports['interactivity'] ) ||
521
+
! empty( $this->block_type->supports['interactivity']['interactive'] )
522
+
)
523
+
) {
524
+
$root_interactive_block = $this;
525
+
}
526
+
527
+
$options = wp_parse_args(
528
+
$options,
529
+
array(
530
+
'dynamic' => true,
531
+
)
532
+
);
533
+
534
+
// Process the block bindings and get attributes updated with the values from the sources.
535
+
$computed_attributes = $this->process_block_bindings();
536
+
if ( ! empty( $computed_attributes ) ) {
537
+
// Merge the computed attributes with the original attributes.
538
+
$this->attributes = array_merge( $this->attributes, $computed_attributes );
539
+
}
540
+
541
+
$is_dynamic = $options['dynamic'] && $this->name && null !== $this->block_type && $this->block_type->is_dynamic();
542
+
$block_content = '';
543
+
544
+
if ( ! $options['dynamic'] || empty( $this->block_type->skip_inner_blocks ) ) {
545
+
$index = 0;
546
+
547
+
foreach ( $this->inner_content as $chunk ) {
548
+
if ( is_string( $chunk ) ) {
549
+
$block_content .= $chunk;
550
+
} else {
551
+
$inner_block = $this->inner_blocks[ $index ];
552
+
$parent_block = $this;
553
+
554
+
/** This filter is documented in wp-includes/blocks.php */
555
+
$pre_render = apply_filters( 'pre_render_block', null, $inner_block->parsed_block, $parent_block );
556
+
557
+
if ( ! is_null( $pre_render ) ) {
558
+
$block_content .= $pre_render;
559
+
} else {
560
+
$source_block = $inner_block->parsed_block;
561
+
$inner_block_context = $inner_block->context;
562
+
563
+
/** This filter is documented in wp-includes/blocks.php */
564
+
$inner_block->parsed_block = apply_filters( 'render_block_data', $inner_block->parsed_block, $source_block, $parent_block );
565
+
566
+
/** This filter is documented in wp-includes/blocks.php */
567
+
$inner_block->context = apply_filters( 'render_block_context', $inner_block->context, $inner_block->parsed_block, $parent_block );
568
+
569
+
/*
570
+
* The `refresh_context_dependents()` method already calls `refresh_parsed_block_dependents()`.
571
+
* Therefore the second condition is irrelevant if the first one is satisfied.
572
+
*/
573
+
if ( $inner_block->context !== $inner_block_context ) {
574
+
$inner_block->refresh_context_dependents();
575
+
} elseif ( $inner_block->parsed_block !== $source_block ) {
576
+
$inner_block->refresh_parsed_block_dependents();
577
+
}
578
+
579
+
$block_content .= $inner_block->render();
580
+
}
581
+
582
+
++$index;
583
+
}
584
+
}
585
+
}
586
+
587
+
if ( ! empty( $computed_attributes ) && ! empty( $block_content ) ) {
588
+
foreach ( $computed_attributes as $attribute_name => $source_value ) {
589
+
$block_content = $this->replace_html( $block_content, $attribute_name, $source_value );
590
+
}
591
+
}
592
+
593
+
if ( $is_dynamic ) {
594
+
$global_post = $post;
595
+
$parent = WP_Block_Supports::$block_to_render;
596
+
597
+
WP_Block_Supports::$block_to_render = $this->parsed_block;
598
+
599
+
$block_content = (string) call_user_func( $this->block_type->render_callback, $this->attributes, $block_content, $this );
600
+
601
+
WP_Block_Supports::$block_to_render = $parent;
602
+
603
+
$post = $global_post;
604
+
}
605
+
606
+
if ( ( ! empty( $this->block_type->script_handles ) ) ) {
607
+
foreach ( $this->block_type->script_handles as $script_handle ) {
608
+
wp_enqueue_script( $script_handle );
609
+
}
610
+
}
611
+
612
+
if ( ! empty( $this->block_type->view_script_handles ) ) {
613
+
foreach ( $this->block_type->view_script_handles as $view_script_handle ) {
614
+
wp_enqueue_script( $view_script_handle );
615
+
}
616
+
}
617
+
618
+
if ( ! empty( $this->block_type->view_script_module_ids ) ) {
619
+
foreach ( $this->block_type->view_script_module_ids as $view_script_module_id ) {
620
+
wp_enqueue_script_module( $view_script_module_id );
621
+
}
622
+
}
623
+
624
+
/*
625
+
* For Core blocks, these styles are only enqueued if `wp_should_load_separate_core_block_assets()` returns
626
+
* true. Otherwise these `wp_enqueue_style()` calls will not have any effect, as the Core blocks are relying on
627
+
* the combined 'wp-block-library' stylesheet instead, which is unconditionally enqueued.
628
+
*/
629
+
if ( ( ! empty( $this->block_type->style_handles ) ) ) {
630
+
foreach ( $this->block_type->style_handles as $style_handle ) {
631
+
wp_enqueue_style( $style_handle );
632
+
}
633
+
}
634
+
635
+
if ( ( ! empty( $this->block_type->view_style_handles ) ) ) {
636
+
foreach ( $this->block_type->view_style_handles as $view_style_handle ) {
637
+
wp_enqueue_style( $view_style_handle );
638
+
}
639
+
}
640
+
641
+
/**
642
+
* Filters the content of a single block.
643
+
*
644
+
* @since 5.0.0
645
+
* @since 5.9.0 The `$instance` parameter was added.
646
+
*
647
+
* @param string $block_content The block content.
648
+
* @param array $block The full block, including name and attributes.
649
+
* @param WP_Block $instance The block instance.
650
+
*/
651
+
$block_content = apply_filters( 'render_block', $block_content, $this->parsed_block, $this );
652
+
653
+
/**
654
+
* Filters the content of a single block.
655
+
*
656
+
* The dynamic portion of the hook name, `$name`, refers to
657
+
* the block name, e.g. "core/paragraph".
658
+
*
659
+
* @since 5.7.0
660
+
* @since 5.9.0 The `$instance` parameter was added.
661
+
*
662
+
* @param string $block_content The block content.
663
+
* @param array $block The full block, including name and attributes.
664
+
* @param WP_Block $instance The block instance.
665
+
*/
666
+
$block_content = apply_filters( "render_block_{$this->name}", $block_content, $this->parsed_block, $this );
667
+
668
+
if ( $root_interactive_block === $this ) {
669
+
// The root interactive block has finished rendering. Time to process directives.
670
+
$block_content = wp_interactivity_process_directives( $block_content );
671
+
$root_interactive_block = null;
672
+
}
673
+
674
+
// Capture the new assets enqueued during rendering, and restore the queues the state prior to rendering.
675
+
$after_styles_queue = wp_styles()->queue;
676
+
$after_scripts_queue = wp_scripts()->queue;
677
+
$after_script_modules_queue = wp_script_modules()->get_queue();
678
+
679
+
/*
680
+
* As a very special case, a dynamic block may in fact include a call to wp_head() (and thus wp_enqueue_scripts()),
681
+
* in which all of its enqueued assets are targeting wp_footer. In this case, nothing would be printed, but this
682
+
* shouldn't indicate that the just-enqueued assets should be dequeued due to it being an empty block.
683
+
*/
684
+
$just_did_wp_enqueue_scripts = ( did_action( 'wp_enqueue_scripts' ) !== $before_wp_enqueue_scripts_count );
685
+
686
+
$has_new_styles = ( $before_styles_queue !== $after_styles_queue );
687
+
$has_new_scripts = ( $before_scripts_queue !== $after_scripts_queue );
688
+
$has_new_script_modules = ( $before_script_modules_queue !== $after_script_modules_queue );
689
+
690
+
// Dequeue the newly enqueued assets with the existing assets if the rendered block was empty & wp_enqueue_scripts did not fire.
691
+
if (
692
+
! $just_did_wp_enqueue_scripts &&
693
+
( $has_new_styles || $has_new_scripts || $has_new_script_modules ) &&
694
+
(
695
+
trim( $block_content ) === '' &&
696
+
/**
697
+
* Filters whether to enqueue assets for a block which has no rendered content.
698
+
*
699
+
* @since 6.9.0
700
+
*
701
+
* @param bool $enqueue Whether to enqueue assets.
702
+
* @param string $block_name Block name.
703
+
*/
704
+
! (bool) apply_filters( 'enqueue_empty_block_content_assets', false, $this->name )
705
+
)
706
+
) {
707
+
foreach ( array_diff( $after_styles_queue, $before_styles_queue ) as $handle ) {
708
+
wp_dequeue_style( $handle );
709
+
}
710
+
foreach ( array_diff( $after_scripts_queue, $before_scripts_queue ) as $handle ) {
711
+
wp_dequeue_script( $handle );
712
+
}
713
+
foreach ( array_diff( $after_script_modules_queue, $before_script_modules_queue ) as $handle ) {
714
+
wp_dequeue_script_module( $handle );
715
+
}
716
+
}
717
+
718
+
return $block_content;
719
+
}
720
+
}
721
+