Skip to content

Commit ab31137

Browse files
shantanu2704GaryJonesclaude
committed
fix: prevent PHP warnings when pull_content receives non-array posts
When a syndication client's get_posts() method returns a non-array value (e.g., false, null), the pull_content() method would trigger PHP warnings from count() and foreach operations on non-array values. Added is_array() checks before array operations to handle cases where the client returns an error or empty response. Also fixed indentation issue where syn_last_pull_time update was incorrectly nested inside the posts processing block, causing it to not update when there were no posts to process. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Gary Jones <gary@garyjones.io> Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ee56255 commit ab31137

File tree

2 files changed

+212
-43
lines changed

2 files changed

+212
-43
lines changed

includes/class-wp-push-syndication-server.php

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,69 +1337,71 @@ public function pull_content( $sites = array() ) {
13371337

13381338
$post_types_processed = array();
13391339

1340-
if ( count( $posts ) > 0 ) {
1340+
if ( is_array( $posts ) && count( $posts ) > 0 ) {
13411341
Syndication_Logger::log_post_info( $site_id, $status = 'start_import', $message = sprintf( __( 'starting import for site id %d with %d posts', 'push-syndication' ), $site_id, count( $posts ) ), $log_time = null, $extra = array() );
13421342
} else {
13431343
Syndication_Logger::log_post_info( $site_id, $status = 'no_posts', $message = sprintf( __( 'no posts for site id %d', 'push-syndication' ), $site_id ), $log_time = null, $extra = array() );
13441344
}
13451345

1346-
foreach( $posts as $post ) {
1346+
if ( is_array( $posts ) && ! empty( $posts) ) {
1347+
foreach( $posts as $post ) {
13471348

1348-
if ( ! in_array( $post['post_type'], $post_types_processed ) ) {
1349-
remove_post_type_support( $post['post_type'], 'revisions' );
1350-
$post_types_processed[] = $post['post_type'];
1351-
}
1352-
1353-
if ( empty( $post['post_guid'] ) ) {
1354-
Syndication_Logger::log_post_error( $site_id, $status = 'no_post_guid', $message = sprintf( __( 'skipping post no guid', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1355-
continue;
1356-
}
1357-
$post_id = $this->find_post_by_guid( $post['post_guid'], $post, $site );
1358-
1359-
if ( $post_id ) {
1360-
$pull_edit_shortcircuit = apply_filters( 'syn_pre_pull_edit_post_shortcircuit', false, $post, $site, $transport_type, $client );
1361-
if ( true === $pull_edit_shortcircuit ) {
1362-
Syndication_Logger::log_post_info( $site_id, $status = 'skip_pre_pull_edit_post', $message = sprintf( __( 'skipping post per syn_pre_pull_edit_post_shortcircuit', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1363-
continue;
1349+
if ( ! in_array( $post['post_type'], $post_types_processed ) ) {
1350+
remove_post_type_support( $post['post_type'], 'revisions' );
1351+
$post_types_processed[] = $post['post_type'];
13641352
}
1365-
// if updation is disabled continue
1366-
if( $this->push_syndicate_settings['update_pulled_posts'] != 'on' ) {
1367-
Syndication_Logger::log_post_info( $site_id, $status = 'skip_update_pulled_posts', $message = sprintf( __( 'skipping post update per update_pulled_posts setting', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1353+
1354+
if ( empty( $post['post_guid'] ) ) {
1355+
Syndication_Logger::log_post_error( $site_id, $status = 'no_post_guid', $message = sprintf( __( 'skipping post no guid', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
13681356
continue;
13691357
}
1370-
$post['ID'] = $post_id;
1358+
$post_id = $this->find_post_by_guid( $post['post_guid'], $post, $site );
13711359

1372-
$post = apply_filters( 'syn_pull_edit_post', $post, $site, $client );
1360+
if ( $post_id ) {
1361+
$pull_edit_shortcircuit = apply_filters( 'syn_pre_pull_edit_post_shortcircuit', false, $post, $site, $transport_type, $client );
1362+
if ( true === $pull_edit_shortcircuit ) {
1363+
Syndication_Logger::log_post_info( $site_id, $status = 'skip_pre_pull_edit_post', $message = sprintf( __( 'skipping post per syn_pre_pull_edit_post_shortcircuit', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1364+
continue;
1365+
}
1366+
// if updation is disabled continue
1367+
if( $this->push_syndicate_settings['update_pulled_posts'] != 'on' ) {
1368+
Syndication_Logger::log_post_info( $site_id, $status = 'skip_update_pulled_posts', $message = sprintf( __( 'skipping post update per update_pulled_posts setting', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1369+
continue;
1370+
}
1371+
$post['ID'] = $post_id;
13731372

1374-
$result = wp_update_post( $post, true );
1373+
$post = apply_filters( 'syn_pull_edit_post', $post, $site, $client );
13751374

1376-
do_action( 'syn_post_pull_edit_post', $result, $post, $site, $transport_type, $client );
1375+
$result = wp_update_post( $post, true );
13771376

1378-
$updated_post_ids[] = (int) $result;
1377+
do_action( 'syn_post_pull_edit_post', $result, $post, $site, $transport_type, $client );
13791378

1380-
} else {
1381-
$pull_new_shortcircuit = apply_filters( 'syn_pre_pull_new_post_shortcircuit', false, $post, $site, $transport_type, $client );
1382-
if ( true === $pull_new_shortcircuit ) {
1383-
Syndication_Logger::log_post_info( $site_id, $status = 'syn_pre_pull_new_post_shortcircuit', $message = sprintf( __( 'skipping post per syn_pre_pull_edit_post_shortcircuit', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1384-
continue;
1385-
}
1386-
$post = apply_filters( 'syn_pull_new_post', $post, $site, $client );
1379+
$updated_post_ids[] = (int) $result;
13871380

1388-
$result = wp_insert_post( $post, true );
1381+
} else {
1382+
$pull_new_shortcircuit = apply_filters( 'syn_pre_pull_new_post_shortcircuit', false, $post, $site, $transport_type, $client );
1383+
if ( true === $pull_new_shortcircuit ) {
1384+
Syndication_Logger::log_post_info( $site_id, $status = 'syn_pre_pull_new_post_shortcircuit', $message = sprintf( __( 'skipping post per syn_pre_pull_edit_post_shortcircuit', 'push-syndication' ) ), $log_time = null, $extra = array( 'post' => $post ) );
1385+
continue;
1386+
}
1387+
$post = apply_filters( 'syn_pull_new_post', $post, $site, $client );
13891388

1390-
do_action( 'syn_post_pull_new_post', $result, $post, $site, $transport_type, $client );
1389+
$result = wp_insert_post( $post, true );
13911390

1392-
if( !is_wp_error( $result ) ) {
1393-
update_post_meta( $result, 'syn_post_guid', $post['post_guid'] );
1394-
update_post_meta( $result, 'syn_source_site_id', $site_id );
1395-
}
1391+
do_action( 'syn_post_pull_new_post', $result, $post, $site, $transport_type, $client );
13961392

1397-
$updated_post_ids[] = (int) $result;
1393+
if( !is_wp_error( $result ) ) {
1394+
update_post_meta( $result, 'syn_post_guid', $post['post_guid'] );
1395+
update_post_meta( $result, 'syn_source_site_id', $site_id );
1396+
}
1397+
1398+
$updated_post_ids[] = (int) $result;
1399+
}
13981400
}
1399-
}
14001401

1401-
foreach ( $post_types_processed as $post_type ) {
1402-
add_post_type_support( $post_type, 'revisions' );
1402+
foreach ( $post_types_processed as $post_type ) {
1403+
add_post_type_support( $post_type, 'revisions' );
1404+
}
14031405
}
14041406

14051407
update_post_meta( $site_id, 'syn_last_pull_time', current_time( 'timestamp', 1 ) );
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
/**
3+
* Tests for the pull_content functionality in WP_Push_Syndication_Server.
4+
*
5+
* @package Automattic\Syndication\Tests
6+
*/
7+
8+
namespace Syndication\Tests;
9+
10+
use Yoast\WPTestUtils\WPIntegration\TestCase as WPIntegrationTestCase;
11+
12+
/**
13+
* Mock client for testing pull_content with various return values.
14+
*/
15+
class Mock_Syndication_Client implements \Syndication_Client {
16+
private static $posts_to_return = array();
17+
18+
public static function set_posts( $posts ) {
19+
self::$posts_to_return = $posts;
20+
}
21+
22+
public function __construct( $site_id ) {}
23+
24+
public function get_posts( $args = array() ) {
25+
return self::$posts_to_return;
26+
}
27+
28+
public function new_post( $post_id ) {
29+
return false;
30+
}
31+
32+
public function edit_post( $post_id, $ext_id ) {
33+
return false;
34+
}
35+
36+
public function delete_post( $ext_id ) {
37+
return false;
38+
}
39+
40+
public function test_connection() {
41+
return true;
42+
}
43+
44+
public function get_post( $ext_id ) {
45+
return false;
46+
}
47+
48+
public function is_post_exists( $ext_id ) {
49+
return false;
50+
}
51+
52+
public static function get_client_data() {
53+
return array();
54+
}
55+
56+
public static function display_settings( $site ) {}
57+
58+
public static function save_settings( $site_id ) {}
59+
}
60+
61+
// Register the mock client class globally.
62+
class_alias( __NAMESPACE__ . '\\Mock_Syndication_Client', 'Syndication_Mock_Client' );
63+
64+
/**
65+
* Class PullContentTest
66+
*
67+
* @covers WP_Push_Syndication_Server::pull_content
68+
*/
69+
class PullContentTest extends WPIntegrationTestCase {
70+
71+
/**
72+
* Test that pull_content handles non-array posts without PHP warnings.
73+
*
74+
* This tests the fix for PHP warnings that occurred when a client's
75+
* get_posts() method returned a non-array value (e.g., false, null).
76+
*
77+
* @covers WP_Push_Syndication_Server::pull_content
78+
*/
79+
public function test_pull_content_handles_non_array_posts(): void {
80+
global $push_syndication_server;
81+
82+
// Configure mock client to return false.
83+
Mock_Syndication_Client::set_posts( false );
84+
85+
// Create a site post with pull enabled.
86+
$site_id = $this->factory()->post->create(
87+
array(
88+
'post_type' => 'syn_site',
89+
'post_status' => 'publish',
90+
)
91+
);
92+
93+
// Enable the site for syndication.
94+
update_post_meta( $site_id, 'syn_site_enabled', 'on' );
95+
96+
// Use our mock transport type.
97+
update_post_meta( $site_id, 'syn_transport_type', 'Mock' );
98+
99+
// Get the site post object to pass directly.
100+
$site = get_post( $site_id );
101+
102+
// This should not trigger any PHP warnings.
103+
// If is_array() check is missing, count() on false would warn.
104+
$push_syndication_server->pull_content( array( $site ) );
105+
106+
// Verify the last pull time was still updated.
107+
$last_pull_time = get_post_meta( $site_id, 'syn_last_pull_time', true );
108+
$this->assertNotEmpty( $last_pull_time, 'Last pull time should be updated even when no posts returned' );
109+
}
110+
111+
/**
112+
* Test that pull_content handles null posts without PHP warnings.
113+
*
114+
* @covers WP_Push_Syndication_Server::pull_content
115+
*/
116+
public function test_pull_content_handles_null_posts(): void {
117+
global $push_syndication_server;
118+
119+
Mock_Syndication_Client::set_posts( null );
120+
121+
$site_id = $this->factory()->post->create(
122+
array(
123+
'post_type' => 'syn_site',
124+
'post_status' => 'publish',
125+
)
126+
);
127+
128+
update_post_meta( $site_id, 'syn_site_enabled', 'on' );
129+
update_post_meta( $site_id, 'syn_transport_type', 'Mock' );
130+
131+
$site = get_post( $site_id );
132+
133+
// Should not trigger warnings.
134+
$push_syndication_server->pull_content( array( $site ) );
135+
136+
$last_pull_time = get_post_meta( $site_id, 'syn_last_pull_time', true );
137+
$this->assertNotEmpty( $last_pull_time );
138+
}
139+
140+
/**
141+
* Test that pull_content handles empty array without issues.
142+
*
143+
* @covers WP_Push_Syndication_Server::pull_content
144+
*/
145+
public function test_pull_content_handles_empty_array(): void {
146+
global $push_syndication_server;
147+
148+
Mock_Syndication_Client::set_posts( array() );
149+
150+
$site_id = $this->factory()->post->create(
151+
array(
152+
'post_type' => 'syn_site',
153+
'post_status' => 'publish',
154+
)
155+
);
156+
157+
update_post_meta( $site_id, 'syn_site_enabled', 'on' );
158+
update_post_meta( $site_id, 'syn_transport_type', 'Mock' );
159+
160+
$site = get_post( $site_id );
161+
162+
$push_syndication_server->pull_content( array( $site ) );
163+
164+
$last_pull_time = get_post_meta( $site_id, 'syn_last_pull_time', true );
165+
$this->assertNotEmpty( $last_pull_time, 'Last pull time should be updated even with empty posts array' );
166+
}
167+
}

0 commit comments

Comments
 (0)