2
2
3
3
namespace WordPress \DataLiberation \BlockMarkup ;
4
4
5
- use RecursiveArrayIterator ;
6
- use RecursiveIteratorIterator ;
7
5
use WP_Block_Parser_Error ;
8
6
use WP_HTML_Tag_Processor ;
9
7
@@ -75,11 +73,22 @@ class BlockMarkupProcessor extends WP_HTML_Tag_Processor {
75
73
private $ last_block_error ;
76
74
77
75
/**
78
- * Iterator for traversing nested block attributes
76
+ * A flattened list of paths (arrays of keys) to every attribute found in
77
+ * $block_attributes. This is used by next_block_attribute() to traverse
78
+ * attributes without relying on PHP iterator classes.
79
79
*
80
- * @var RecursiveIteratorIterator
80
+ * @var array<int, array<int|string>>|null
81
81
*/
82
- private $ block_attributes_iterator ;
82
+ private $ block_attribute_paths = null ;
83
+
84
+ /**
85
+ * The index of the current attribute inside $block_attribute_paths.
86
+ * Starts at -1 so that the first call to next_block_attribute() positions
87
+ * the cursor at index 0.
88
+ *
89
+ * @var int
90
+ */
91
+ private $ block_attribute_index = -1 ;
83
92
84
93
/**
85
94
* Gets the type of the current token, adding a special '#block-comment' type
@@ -287,7 +296,8 @@ public function next_token(): bool {
287
296
288
297
$ this ->block_name = null ;
289
298
$ this ->block_attributes = null ;
290
- $ this ->block_attributes_iterator = null ;
299
+ $ this ->block_attribute_paths = null ;
300
+ $ this ->block_attribute_index = -1 ;
291
301
$ this ->block_closer = false ;
292
302
$ this ->self_closing_flag = false ;
293
303
$ this ->block_attributes_updated = false ;
@@ -469,19 +479,19 @@ private function block_attribute_updates_to_modifiable_text_updates() {
469
479
if ( ! $ this ->block_attributes_updated ) {
470
480
return false ;
471
481
}
482
+
472
483
$ encoded_attributes = json_encode (
473
- $ this ->block_attributes_iterator
474
- ? $ this ->block_attributes_iterator ->getSubIterator ( 0 )->getArrayCopy ()
475
- : $ this ->block_attributes ,
484
+ $ this ->block_attributes ,
476
485
JSON_HEX_TAG | // Convert < and > to \u003C and \u003E
477
486
JSON_HEX_AMP // Convert & to \u0026
478
487
);
479
- var_dump ( $ encoded_attributes );
488
+
480
489
if ( $ encoded_attributes === '[] ' ) {
481
490
$ encoded_attributes = '' ;
482
491
} else {
483
492
$ encoded_attributes .= ' ' ;
484
493
}
494
+
485
495
$ this ->set_modifiable_text (
486
496
' ' .
487
497
$ this ->block_name .
@@ -502,28 +512,19 @@ public function next_block_attribute() {
502
512
return false ;
503
513
}
504
514
505
- if ( null === $ this ->block_attributes_iterator ) {
515
+ if ( null === $ this ->block_attribute_paths ) {
506
516
$ block_attributes = $ this ->get_block_attributes ();
507
517
if ( ! is_array ( $ block_attributes ) ) {
508
518
return false ;
509
519
}
510
- // Re-entrant iteration over the block attributes.
511
- $ this ->block_attributes_iterator = new RecursiveIteratorIterator (
512
- new RecursiveArrayIterator ( $ block_attributes ),
513
- RecursiveIteratorIterator::SELF_FIRST
514
- );
515
- }
516
-
517
- while ( true ) {
518
- $ this ->block_attributes_iterator ->next ();
519
- if ( ! $ this ->block_attributes_iterator ->valid () ) {
520
- break ;
521
- }
522
520
523
- return true ;
521
+ $ this ->block_attribute_paths = $ this ->build_block_attribute_paths ( $ block_attributes );
522
+ $ this ->block_attribute_index = -1 ;
524
523
}
525
524
526
- return false ;
525
+ $ this ->block_attribute_index ++;
526
+
527
+ return isset ( $ this ->block_attribute_paths [ $ this ->block_attribute_index ] );
527
528
}
528
529
529
530
/**
@@ -532,11 +533,13 @@ public function next_block_attribute() {
532
533
* @return string|false The attribute key or false if no attribute was matched
533
534
*/
534
535
public function get_block_attribute_key () {
535
- if ( null === $ this ->block_attributes_iterator || false === $ this ->block_attributes_iterator -> valid ( ) ) {
536
+ if ( null === $ this ->block_attribute_paths || ! isset ( $ this ->block_attribute_paths [ $ this -> block_attribute_index ] ) ) {
536
537
return false ;
537
538
}
538
539
539
- return $ this ->block_attributes_iterator ->key ();
540
+ $ path = $ this ->block_attribute_paths [ $ this ->block_attribute_index ];
541
+
542
+ return $ path [ count ( $ path ) - 1 ];
540
543
}
541
544
542
545
/**
@@ -545,11 +548,21 @@ public function get_block_attribute_key() {
545
548
* @return mixed|false The attribute value or false if no attribute was matched
546
549
*/
547
550
public function get_block_attribute_value () {
548
- if ( null === $ this ->block_attributes_iterator || false === $ this ->block_attributes_iterator -> valid ( ) ) {
551
+ if ( null === $ this ->block_attribute_paths || ! isset ( $ this ->block_attribute_paths [ $ this -> block_attribute_index ] ) ) {
549
552
return false ;
550
553
}
551
554
552
- return $ this ->block_attributes_iterator ->current ();
555
+ $ path = $ this ->block_attribute_paths [ $ this ->block_attribute_index ];
556
+ $ value = $ this ->block_attributes ;
557
+
558
+ foreach ( $ path as $ segment ) {
559
+ if ( ! is_array ( $ value ) || ! array_key_exists ( $ segment , $ value ) ) {
560
+ return false ;
561
+ }
562
+ $ value = $ value [ $ segment ];
563
+ }
564
+
565
+ return $ value ;
553
566
}
554
567
555
568
/**
@@ -560,18 +573,49 @@ public function get_block_attribute_value() {
560
573
* @return bool Whether the value was successfully set
561
574
*/
562
575
public function set_block_attribute_value ( $ new_value ) {
563
- if ( null === $ this ->block_attributes_iterator || false === $ this ->block_attributes_iterator -> valid ( ) ) {
576
+ if ( null === $ this ->block_attribute_paths || ! isset ( $ this ->block_attribute_paths [ $ this -> block_attribute_index ] ) ) {
564
577
return false ;
565
578
}
566
579
567
- $ this ->block_attributes_iterator
568
- ->getSubIterator ( $ this ->block_attributes_iterator ->getDepth () )
569
- ->offsetSet (
570
- $ this ->get_block_attribute_key (),
571
- $ new_value
572
- );
580
+ $ path = $ this ->block_attribute_paths [ $ this ->block_attribute_index ];
581
+
582
+ $ ref =& $ this ->block_attributes ;
583
+ $ depth = count ( $ path );
584
+ for ( $ i = 0 ; $ i < $ depth - 1 ; $ i ++ ) {
585
+ $ segment = $ path [ $ i ];
586
+ if ( ! is_array ( $ ref ) || ! array_key_exists ( $ segment , $ ref ) ) {
587
+ return false ; // Path is invalid.
588
+ }
589
+ $ ref =& $ ref [ $ segment ];
590
+ }
591
+
592
+ $ last_key = $ path [ $ depth - 1 ];
593
+ $ ref [ $ last_key ] = $ new_value ;
573
594
$ this ->block_attributes_updated = true ;
574
595
575
596
return true ;
576
597
}
598
+
599
+ /**
600
+ * Builds a list of attribute paths, using a depth-first, SELF_FIRST order
601
+ * that matches the previous iterator behaviour.
602
+ *
603
+ * @param array<int|string, mixed> $attributes
604
+ * @param array<int|string> $base_path
605
+ * @return array<int, array<int|string>>
606
+ */
607
+ private function build_block_attribute_paths ( $ attributes , $ base_path = array () ) {
608
+ $ paths = array ();
609
+
610
+ foreach ( $ attributes as $ key => $ value ) {
611
+ $ current_path = array_merge ( $ base_path , array ( $ key ) );
612
+ $ paths [] = $ current_path ; // SELF_FIRST: include parent before children.
613
+
614
+ if ( is_array ( $ value ) ) {
615
+ $ paths = array_merge ( $ paths , $ this ->build_block_attribute_paths ( $ value , $ current_path ) );
616
+ }
617
+ }
618
+
619
+ return $ paths ;
620
+ }
577
621
}
0 commit comments