Skip to content

Commit 9292f67

Browse files
Widget Visibility: Fix thrown PHP error under specific block conditions (#45087)
1 parent eda78d8 commit 9292f67

File tree

3 files changed

+115
-23
lines changed

3 files changed

+115
-23
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: bugfix
3+
4+
Widget Visibility: Fix thrown PHP error under specific block conditions

projects/plugins/jetpack/modules/widget-visibility/widget-conditions.php

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,40 @@ public static function generate_condition_key( $rule ) {
799799
return $rule['major'] . ':' . $rule['minor'];
800800
}
801801

802+
/**
803+
* Normalize widget `content` into a string suitable for block scanning.
804+
*
805+
* @since $$next-version$$
806+
*
807+
* @param mixed $content The widget instance 'content' value.
808+
* @return string|false Normalized string content or false if none.
809+
*/
810+
private static function normalize_widget_content( $content ) {
811+
if ( empty( $content ) ) {
812+
return false;
813+
}
814+
815+
if ( is_string( $content ) ) {
816+
return $content;
817+
}
818+
819+
if ( ! is_array( $content ) ) {
820+
return false;
821+
}
822+
823+
if ( isset( $content['content'] ) && is_string( $content['content'] ) ) {
824+
return $content['content'];
825+
}
826+
827+
if ( isset( $content[0] ) && is_array( $content[0] ) && isset( $content[0]['blockName'] ) ) {
828+
// Looks like a parsed blocks array.
829+
return serialize_blocks( $content );
830+
}
831+
832+
// Unknown array shape: treat as no visibility rules.
833+
return false;
834+
}
835+
802836
/**
803837
* Determine whether the widget should be displayed based on conditions set by the user.
804838
*
@@ -826,36 +860,45 @@ public static function filter_widget( $instance ) {
826860
return $instance;
827861
}
828862
return false;
829-
} elseif ( ! empty( $instance['content'] ) && has_blocks( $instance['content'] ) ) {
830-
$scanner = Block_Scanner::create( $instance['content'] );
831-
if ( ! $scanner ) {
832-
// No Rules: Display widget.
833-
return $instance;
834-
}
863+
}
835864

836-
// Find the first block that opens
837-
while ( $scanner->next_delimiter() ) {
838-
if ( ! $scanner->opens_block() ) {
839-
continue;
840-
}
865+
if ( empty( $instance['content'] ) ) {
866+
return $instance;
867+
}
868+
$content = self::normalize_widget_content( isset( $instance['content'] ) ? $instance['content'] : null );
841869

842-
$attributes = $scanner->allocate_and_return_parsed_attributes();
870+
if ( false === $content || ! has_blocks( $content ) ) {
871+
// No visibility found.
872+
return $instance;
873+
}
843874

844-
if ( ! is_array( $attributes ) || empty( $attributes['conditions']['rules'] ) ) {
845-
// No Rules: Display widget.
846-
return $instance;
847-
}
875+
$scanner = Block_Scanner::create( $content );
876+
if ( ! $scanner ) {
877+
// No Rules: Display widget.
878+
return $instance;
879+
}
848880

849-
if ( self::filter_widget_check_conditions( $attributes['conditions'] ) ) {
850-
// Rules passed checks: Display widget.
851-
return $instance;
852-
}
881+
// Find the first block that opens
882+
while ( $scanner->next_delimiter() ) {
883+
if ( ! $scanner->opens_block() ) {
884+
continue;
885+
}
886+
887+
$attributes = $scanner->allocate_and_return_parsed_attributes();
853888

854-
// Rules failed checks: Hide widget.
855-
return false;
889+
if ( ! is_array( $attributes ) || empty( $attributes['conditions']['rules'] ) ) {
890+
// No Rules: Display widget.
891+
return $instance;
856892
}
857-
}
858893

894+
if ( self::filter_widget_check_conditions( $attributes['conditions'] ) ) {
895+
// Rules passed checks: Display widget.
896+
return $instance;
897+
}
898+
899+
// Rules failed checks: Hide widget.
900+
return false;
901+
}
859902
// No visibility found.
860903
return $instance;
861904
}

projects/plugins/jetpack/tests/php/modules/widget-visibility/Jetpack_Widget_Conditions_Test.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,49 @@ public function test_filter_widget() {
5959
$return_val = Jetpack_Widget_Conditions::filter_widget( $block );
6060
$this->assertSame( $expected, $return_val );
6161
}
62+
63+
/**
64+
* Verifies that filter_widget handles parsed blocks arrays (output of parse_blocks)
65+
* and respects visibility rules without throwing errors.
66+
*/
67+
public function test_filter_widget_with_parsed_blocks_array() {
68+
// Block with rule for "Display only when logged out" (Will pass during unit tests).
69+
$block_content = '<!-- wp:paragraph {"conditions":{"action":"show","rules":[{"major":"loggedin","minor":"loggedout"}],"match_all":0}} -->'
70+
. "\n" . '<p>Test Paragraph</p>'
71+
. "\n" . '<!-- /wp:paragraph -->';
72+
$parsed_blocks = parse_blocks( $block_content );
73+
$block = array( 'content' => $parsed_blocks );
74+
$expected = unserialize( serialize( $block ) );
75+
$return_val = Jetpack_Widget_Conditions::filter_widget( $block );
76+
$this->assertSame( $expected, $return_val );
77+
78+
// Block with rule for "Display only when logged in" (Will fail during unit tests).
79+
$block_content = '<!-- wp:paragraph {"conditions":{"action":"show","rules":[{"major":"loggedin","minor":"loggedin"}],"match_all":0}} -->'
80+
. "\n" . '<p>Test Paragraph</p>'
81+
. "\n" . '<!-- /wp:paragraph -->';
82+
$parsed_blocks = parse_blocks( $block_content );
83+
$block = array( 'content' => $parsed_blocks );
84+
$expected = false;
85+
$return_val = Jetpack_Widget_Conditions::filter_widget( $block );
86+
$this->assertSame( $expected, $return_val );
87+
}
88+
89+
/**
90+
* Verifies that filter_widget safely bails and returns the instance when content
91+
* is an unknown array shape that cannot be normalized.
92+
*/
93+
public function test_filter_widget_with_unknown_array_shape() {
94+
$block = array( 'content' => array( 'foo' => 'bar' ) );
95+
$expected = unserialize( serialize( $block ) );
96+
$this->assertSame( $expected, Jetpack_Widget_Conditions::filter_widget( $block ) );
97+
}
98+
99+
/**
100+
* Verifies that filter_widget safely returns the instance when content is an empty array.
101+
*/
102+
public function test_filter_widget_with_empty_array_content() {
103+
$block = array( 'content' => array() );
104+
$expected = unserialize( serialize( $block ) );
105+
$this->assertSame( $expected, Jetpack_Widget_Conditions::filter_widget( $block ) );
106+
}
62107
}

0 commit comments

Comments
 (0)