* Parsing the post, external links (if any) are stored in the $post_links array.
$post_links_temp = wp_extract_urls( $content );
* Walking through the links array.
* First we get rid of links pointing to sites, not to specific files.
* http://dummy-weblog.org
* http://dummy-weblog.org/
* http://dummy-weblog.org/post.php
* We don't wanna ping first and second types, even if they have a valid <link/>.
foreach ( (array) $post_links_temp as $link_test ) {
// If we haven't pung it already and it isn't a link to itself.
if ( ! in_array( $link_test, $pung, true ) && ( url_to_postid( $link_test ) != $post->ID )
// Also, let's never ping local attachments.
&& ! is_local_attachment( $link_test )
$test = parse_url( $link_test );
if ( isset( $test['query'] ) ) {
$post_links[] = $link_test;
} elseif ( isset( $test['path'] ) && ( '/' !== $test['path'] ) && ( '' !== $test['path'] ) ) {
$post_links[] = $link_test;
$post_links = array_unique( $post_links );
* Fires just before pinging back links found in a post.
* @param string[] $post_links Array of link URLs to be checked (passed by reference).
* @param string[] $pung Array of link URLs already pinged (passed by reference).
* @param int $post_ID The post ID.
do_action_ref_array( 'pre_ping', array( &$post_links, &$pung, $post->ID ) );
foreach ( (array) $post_links as $pagelinkedto ) {
$pingback_server_url = discover_pingback_server_uri( $pagelinkedto );
if ( $pingback_server_url ) {
$pagelinkedfrom = get_permalink( $post );
// Using a timeout of 3 seconds should be enough to cover slow servers.
$client = new WP_HTTP_IXR_Client( $pingback_server_url );
* Filters the user agent sent when pinging-back a URL.
* @param string $concat_useragent The user agent concatenated with ' -- WordPress/'
* and the WordPress version.
* @param string $useragent The useragent.
* @param string $pingback_server_url The server URL being linked to.
* @param string $pagelinkedto URL of page linked to.
* @param string $pagelinkedfrom URL of page linked from.
$client->useragent = apply_filters( 'pingback_useragent', $client->useragent . ' -- WordPress/' . get_bloginfo( 'version' ), $client->useragent, $pingback_server_url, $pagelinkedto, $pagelinkedfrom );
// When set to true, this outputs debug messages by itself.
if ( $client->query( 'pingback.ping', $pagelinkedfrom, $pagelinkedto ) || ( isset( $client->error->code ) && 48 == $client->error->code ) ) { // Already registered.
add_ping( $post, $pagelinkedto );
* Check whether blog is public before returning sites.
* @param mixed $sites Will return if blog is public, will not return if not public.
* @return mixed Empty string if blog is not public, returns $sites, if site is public.
function privacy_ping_filter( $sites ) {
if ( '0' != get_option( 'blog_public' ) ) {
* Updates database when sending trackback to prevent duplicates.
* @global wpdb $wpdb WordPress database abstraction object.
* @param string $trackback_url URL to send trackbacks.
* @param string $title Title of post.
* @param string $excerpt Excerpt of post.
* @param int $ID Post ID.
* @return int|false|void Database query from update.
function trackback( $trackback_url, $title, $excerpt, $ID ) {
if ( empty( $trackback_url ) ) {
$options['timeout'] = 10;
$options['body'] = array(
'url' => get_permalink( $ID ),
'blog_name' => get_option( 'blogname' ),
$response = wp_safe_remote_post( $trackback_url, $options );
if ( is_wp_error( $response ) ) {
$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', %s) WHERE ID = %d", $trackback_url, $ID ) );
return $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $trackback_url, $ID ) );
* @param string $server Host of blog to connect to.
* @param string $path Path to send the ping.
function weblog_ping( $server = '', $path = '' ) {
include_once ABSPATH . WPINC . '/class-IXR.php';
include_once ABSPATH . WPINC . '/class-wp-http-ixr-client.php';
// Using a timeout of 3 seconds should be enough to cover slow servers.
$client = new WP_HTTP_IXR_Client( $server, ( ( ! strlen( trim( $path ) ) || ( '/' === $path ) ) ? false : $path ) );
$client->useragent .= ' -- WordPress/' . get_bloginfo( 'version' );
// When set to true, this outputs debug messages by itself.
$home = trailingslashit( home_url() );
if ( ! $client->query( 'weblogUpdates.extendedPing', get_option( 'blogname' ), $home, get_bloginfo( 'rss2_url' ) ) ) { // Then try a normal ping.
$client->query( 'weblogUpdates.ping', get_option( 'blogname' ), $home );
* Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI
* @see wp_http_validate_url()
* @param string $source_uri
function pingback_ping_source_uri( $source_uri ) {
return (string) wp_http_validate_url( $source_uri );
* Default filter attached to xmlrpc_pingback_error.
* Returns a generic pingback error code unless the error code is 48,
* which reports that the pingback is already registered.
* @link https://www.hixie.ch/specs/pingback/pingback#TOC3
* @param IXR_Error $ixr_error
function xmlrpc_pingback_error( $ixr_error ) {
if ( 48 === $ixr_error->code ) {
return new IXR_Error( 0, '' );
* Removes a comment from the object cache.
* @param int|array $ids Comment ID or an array of comment IDs to remove from cache.
function clean_comment_cache( $ids ) {
foreach ( (array) $ids as $id ) {
wp_cache_delete( $id, 'comment' );
* Fires immediately after a comment has been removed from the object cache.
* @param int $id Comment ID.
do_action( 'clean_comment_cache', $id );
wp_cache_set( 'last_changed', microtime(), 'comment' );
* Updates the comment cache of given comments.
* Will add the comments in $comments to the cache. If comment ID already exists
* in the comment cache then it will not be updated. The comment is added to the
* cache using the comment group with the key using the ID of the comments.
* @since 4.4.0 Introduced the `$update_meta_cache` parameter.
* @param WP_Comment[] $comments Array of comment objects
* @param bool $update_meta_cache Whether to update commentmeta cache. Default true.
function update_comment_cache( $comments, $update_meta_cache = true ) {
foreach ( (array) $comments as $comment ) {
wp_cache_add( $comment->comment_ID, $comment, 'comment' );
if ( $update_meta_cache ) {
// Avoid `wp_list_pluck()` in case `$comments` is passed by reference.
foreach ( $comments as $comment ) {
$comment_ids[] = $comment->comment_ID;
update_meta_cache( 'comment', $comment_ids );
* Adds any comments from the given IDs to the cache that do not already exist in cache.
* @see update_comment_cache()
* @global wpdb $wpdb WordPress database abstraction object.
* @param int[] $comment_ids Array of comment IDs.
* @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
function _prime_comment_caches( $comment_ids, $update_meta_cache = true ) {
$non_cached_ids = _get_non_cached_ids( $comment_ids, 'comment' );
if ( ! empty( $non_cached_ids ) ) {
$fresh_comments = $wpdb->get_results( sprintf( "SELECT $wpdb->comments.* FROM $wpdb->comments WHERE comment_ID IN (%s)", implode( ',', array_map( 'intval', $non_cached_ids ) ) ) );
update_comment_cache( $fresh_comments, $update_meta_cache );
* Close comments on old posts on the fly, without any extra DB queries. Hooked to the_posts.
* @param WP_Post $posts Post data object.
* @param WP_Query $query Query object.
function _close_comments_for_old_posts( $posts, $query ) {
if ( empty( $posts ) || ! $query->is_singular() || ! get_option( 'close_comments_for_old_posts' ) ) {
* Filters the list of post types to automatically close comments for.
* @param string[] $post_types An array of post type names.
$post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
if ( ! in_array( $posts[0]->post_type, $post_types, true ) ) {
$days_old = (int) get_option( 'close_comments_days_old' );
if ( time() - strtotime( $posts[0]->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) {
$posts[0]->comment_status = 'closed';
$posts[0]->ping_status = 'closed';
* Close comments on an old post. Hooked to comments_open and pings_open.
* @param bool $open Comments open or closed.
* @param int $post_id Post ID.
function _close_comments_for_old_post( $open, $post_id ) {
if ( ! get_option( 'close_comments_for_old_posts' ) ) {
$days_old = (int) get_option( 'close_comments_days_old' );
$post = get_post( $post_id );
/** This filter is documented in wp-includes/comment.php */
$post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
if ( ! in_array( $post->post_type, $post_types, true ) ) {
// Undated drafts should not show up as comments closed.
if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
if ( time() - strtotime( $post->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) {
* Handles the submission of a comment, usually posted to wp-comments-post.php via a comment form.
* This function expects unslashed data, as opposed to functions such as `wp_new_comment()` which
* @param array $comment_data {
* @type string|int $comment_post_ID The ID of the post that relates to the comment.
* @type string $author The name of the comment author.
* @type string $email The comment author email address.
* @type string $url The comment author URL.
* @type string $comment The content of the comment.
* @type string|int $comment_parent The ID of this comment's parent, if any. Default 0.
* @type string $_wp_unfiltered_html_comment The nonce value for allowing unfiltered HTML.
* @return WP_Comment|WP_Error A WP_Comment object on success, a WP_Error object on failure.
function wp_handle_comment_submission( $comment_data ) {
$comment_author_email = null;
$comment_author_url = null;
if ( isset( $comment_data['comment_post_ID'] ) ) {
$comment_post_ID = (int) $comment_data['comment_post_ID'];
if ( isset( $comment_data['author'] ) && is_string( $comment_data['author'] ) ) {
$comment_author = trim( strip_tags( $comment_data['author'] ) );
if ( isset( $comment_data['email'] ) && is_string( $comment_data['email'] ) ) {
$comment_author_email = trim( $comment_data['email'] );
if ( isset( $comment_data['url'] ) && is_string( $comment_data['url'] ) ) {
$comment_author_url = trim( $comment_data['url'] );
if ( isset( $comment_data['comment'] ) && is_string( $comment_data['comment'] ) ) {
$comment_content = trim( $comment_data['comment'] );
if ( isset( $comment_data['comment_parent'] ) ) {
$comment_parent = absint( $comment_data['comment_parent'] );
$post = get_post( $comment_post_ID );
if ( empty( $post->comment_status ) ) {
* Fires when a comment is attempted on a post that does not exist.
* @param int $comment_post_ID Post ID.
do_action( 'comment_id_not_found', $comment_post_ID );
return new WP_Error( 'comment_id_not_found' );
// get_post_status() will get the parent status for attachments.
$status = get_post_status( $post );
if ( ( 'private' === $status ) && ! current_user_can( 'read_post', $comment_post_ID ) ) {
return new WP_Error( 'comment_id_not_found' );
$status_obj = get_post_status_object( $status );
if ( ! comments_open( $comment_post_ID ) ) {
* Fires when a comment is attempted on a post that has comments closed.
* @param int $comment_post_ID Post ID.
do_action( 'comment_closed', $comment_post_ID );
return new WP_Error( 'comment_closed', __( 'Sorry, comments are closed for this item.' ), 403 );
} elseif ( 'trash' === $status ) {
* Fires when a comment is attempted on a trashed post.
* @param int $comment_post_ID Post ID.
do_action( 'comment_on_trash', $comment_post_ID );
return new WP_Error( 'comment_on_trash' );
} elseif ( ! $status_obj->public && ! $status_obj->private ) {
* Fires when a comment is attempted on a post in draft mode.
* @param int $comment_post_ID Post ID.
do_action( 'comment_on_draft', $comment_post_ID );
if ( current_user_can( 'read_post', $comment_post_ID ) ) {
return new WP_Error( 'comment_on_draft', __( 'Sorry, comments are not allowed for this item.' ), 403 );
return new WP_Error( 'comment_on_draft' );
} elseif ( post_password_required( $comment_post_ID ) ) {
* Fires when a comment is attempted on a password-protected post.
* @param int $comment_post_ID Post ID.
do_action( 'comment_on_password_protected', $comment_post_ID );
return new WP_Error( 'comment_on_password_protected' );
* Fires before a comment is posted.