: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( ! current_user_can( 'delete_term', $category_id ) ) {
return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this category.' ) );
$status = wp_delete_term( $category_id, 'category' );
* Fires after a category has been successfully deleted via XML-RPC.
* @param int $category_id ID of the deleted category.
* @param array $args An array of arguments to delete the category.
do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
* Retrieves category list.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type array $3 Category
* @type int $4 Max number of results.
* @return array|IXR_Error
public function wp_suggestCategories( $args ) {
$max_results = (int) $args[4];
$user = $this->login( $username, $password );
if ( ! current_user_can( 'edit_posts' ) ) {
return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'wp.suggestCategories', $args, $this );
$category_suggestions = array();
'number' => $max_results,
'name__like' => $category,
foreach ( (array) get_categories( $args ) as $cat ) {
$category_suggestions[] = array(
'category_id' => $cat->term_id,
'category_name' => $cat->name,
return $category_suggestions;
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type int $3 Comment ID.
* @return array|IXR_Error
public function wp_getComment( $args ) {
$comment_id = (int) $args[3];
$user = $this->login( $username, $password );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'wp.getComment', $args, $this );
$comment = get_comment( $comment_id );
return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) );
return $this->_prepare_comment( $comment );
* Besides the common blog_id (unused), username, and password arguments,
* it takes a filter array as the last argument.
* Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'.
* The defaults are as follows:
* - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold')
* - 'post_id' - Default is ''. The post where the comment is posted.
* Empty string shows all comments.
* - 'number' - Default is 10. Total number of media items to retrieve.
* - 'offset' - Default is 0. See WP_Query::query() for more.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type array $3 Optional. Query arguments.
* @return array|IXR_Error Array containing a collection of comments.
* See wp_xmlrpc_server::wp_getComment() for a description
public function wp_getComments( $args ) {
$struct = isset( $args[3] ) ? $args[3] : array();
$user = $this->login( $username, $password );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'wp.getComments', $args, $this );
if ( isset( $struct['status'] ) ) {
$status = $struct['status'];
if ( ! current_user_can( 'moderate_comments' ) && 'approve' !== $status ) {
return new IXR_Error( 401, __( 'Invalid comment status.' ) );
if ( isset( $struct['post_id'] ) ) {
$post_id = absint( $struct['post_id'] );
if ( isset( $struct['post_type'] ) ) {
$post_type_object = get_post_type_object( $struct['post_type'] );
if ( ! $post_type_object || ! post_type_supports( $post_type_object->name, 'comments' ) ) {
return new IXR_Error( 404, __( 'Invalid post type.' ) );
$post_type = $struct['post_type'];
if ( isset( $struct['offset'] ) ) {
$offset = absint( $struct['offset'] );
if ( isset( $struct['number'] ) ) {
$number = absint( $struct['number'] );
$comments = get_comments(
'post_type' => $post_type,
$comments_struct = array();
if ( is_array( $comments ) ) {
foreach ( $comments as $comment ) {
$comments_struct[] = $this->_prepare_comment( $comment );
* By default, the comment will be moved to the Trash instead of deleted.
* See wp_delete_comment() for more information on this behavior.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type int $3 Comment ID.
* @return bool|IXR_Error See wp_delete_comment().
public function wp_deleteComment( $args ) {
$comment_id = (int) $args[3];
$user = $this->login( $username, $password );
if ( ! get_comment( $comment_id ) ) {
return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
return new IXR_Error( 403, __( 'Sorry, you are not allowed to delete this comment.' ) );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'wp.deleteComment', $args, $this );
$status = wp_delete_comment( $comment_id );
* Fires after a comment has been successfully deleted via XML-RPC.
* @param int $comment_id ID of the deleted comment.
* @param array $args An array of arguments to delete the comment.
do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
* Besides the common blog_id (unused), username, and password arguments,
* it takes a comment_id integer and a content_struct array as the last argument.
* The allowed keys in the content_struct array are:
* - 'status'. Common statuses are 'approve', 'hold', 'spam'. See get_comment_statuses() for more details.
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type int $3 Comment ID.
* @type array $4 Content structure.
* @return true|IXR_Error True, on success.
public function wp_editComment( $args ) {
$comment_id = (int) $args[3];
$content_struct = $args[4];
$user = $this->login( $username, $password );
if ( ! get_comment( $comment_id ) ) {
return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) );
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
do_action( 'xmlrpc_call', 'wp.editComment', $args, $this );
'comment_ID' => $comment_id,
if ( isset( $content_struct['status'] ) ) {
$statuses = get_comment_statuses();
$statuses = array_keys( $statuses );
if ( ! in_array( $content_struct['status'], $statuses, true ) ) {
return new IXR_Error( 401, __( 'Invalid comment status.' ) );
$comment['comment_approved'] = $content_struct['status'];
// Do some timestamp voodoo.
if ( ! empty( $content_struct['date_created_gmt'] ) ) {
// We know this is supposed to be GMT, so we're going to slap that Z on there by force.
$dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
$comment['comment_date'] = get_date_from_gmt( $dateCreated );
$comment['comment_date_gmt'] = iso8601_to_datetime( $dateCreated, 'gmt' );
if ( isset( $content_struct['content'] ) ) {
$comment['comment_content'] = $content_struct['content'];
if ( isset( $content_struct['author'] ) ) {
$comment['comment_author'] = $content_struct['author'];
if ( isset( $content_struct['author_url'] ) ) {
$comment['comment_author_url'] = $content_struct['author_url'];
if ( isset( $content_struct['author_email'] ) ) {
$comment['comment_author_email'] = $content_struct['author_email'];
$result = wp_update_comment( $comment, true );
if ( is_wp_error( $result ) ) {
return new IXR_Error( 500, $result->get_error_message() );
return new IXR_Error( 500, __( 'Sorry, the comment could not be updated.' ) );
* Fires after a comment has been successfully updated via XML-RPC.
* @param int $comment_id ID of the updated comment.
* @param array $args An array of arguments to update the comment.
do_action( 'xmlrpc_call_success_wp_editComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
* Method arguments. Note: arguments must be ordered as documented.
* @type int $0 Blog ID (unused).
* @type string $1 Username.
* @type string $2 Password.
* @type string|int $3 Post ID or URL.
* @type array $4 Content structure.
* @return int|IXR_Error See wp_new_comment().
public function wp_newComment( $args ) {
$content_struct = $args[4];
* Filters whether to allow anonymous comments over XML-RPC.
* @param bool $allow Whether to allow anonymous commenting via XML-RPC.
$allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false );
$user = $this->login( $username, $password );
if ( $allow_anon && get_option( 'comment_registration' ) ) {
return new IXR_Error( 403, __( 'Sorry, you must be logged in to comment.' ) );
} elseif ( ! $allow_anon ) {
if ( is_numeric( $post ) ) {
$post_id = absint( $post );
$post_id = url_to_postid( $post );
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
if ( ! get_post( $post_id ) ) {
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
if ( ! comments_open( $post_id ) ) {
return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) );
'publish' === get_post_status( $post_id ) &&
! current_user_can( 'edit_post', $post_id ) &&
post_password_required( $post_id )
return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
'private' === get_post_status( $post_id ) &&
! current_user_can( 'read_post', $post_id )
return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
'comment_post_ID' => $post_id,
'comment_content' => trim( $content_struct['content'] ),
$display_name = $user->display_name;
$user_email = $user->user_email;
$user_url = $user->user_url;
$comment['comment_author'] = $this->escape( $display_name );
$comment['comment_author_email'] = $this->escape( $user_email );
$comment['comment_author_url'] = $this->escape( $user_url );
$comment['user_id'] = $user->ID;
$comment['comment_author'] = '';
if ( isset( $content_struct['author'] ) ) {
$comment['comment_author'] = $content_struct['author'];
$comment['comment_author_email'] = '';
if ( isset( $content_struct['author_email'] ) ) {
$comment['comment_author_email'] = $content_struct['author_email'];
$comment['comment_author_url'] = '';
if ( isset( $content_struct['author_url'] ) ) {
$comment['comment_author_url'] = $content_struct['author_url'];
if ( get_option( 'require_name_email' ) ) {
if ( strlen( $comment['comment_author_email'] ) < 6 || '' === $comment['comment_author'] ) {
return new IXR_Error( 403, __( 'Comment author name and email are required.' ) );
} elseif ( ! is_email( $comment['comment_author_email'] ) ) {
return new IXR_Error( 403, __( 'A valid email address is required.' ) );