Skip to content

Support for WP REST Cache #1630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/1630-from-description
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Added support for the WP Rest Cache plugin to help with caching REST API responses.
145 changes: 145 additions & 0 deletions integration/class-wp-rest-cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php
/**
* WP REST Cache integration file.
*
* This file contains code for caching ActivityPub REST requests.
*
* Copyright (C) 2025 Epiphyt
* Original code: https://epiph.yt/en/blog/2025/accidental-ddos-through-activitypub-plugin/
*
* Portions of this code are adapted from GPL v2 licensed code.
* As such, you may also redistribute and/or modify those portions under the terms of
* the GNU General Public License as published by the Free Software Foundation.
*
* https://www.gnu.org/licenses/gpl-2.0.html
*
* @package Activitypub
*/

namespace Activitypub\Integration;

use Activitypub\Collection\Outbox;
use Activitypub\Comment;
use WP_Rest_Cache_Plugin\Includes\Caching\Caching;

/**
* Compatibility with the WP REST Cache plugin.
*
* @see https://wordpress.org/plugins/wp-rest-cache/
*/
class WP_Rest_Cache {
/**
* Initialize the class, registering WordPress hooks.
*/
public static function init() {
\add_filter( 'wp_rest_cache/allowed_endpoints', array( self::class, 'add_activitypub_endpoints' ) );
\add_filter( 'wp_rest_cache/determine_object_type', array( self::class, 'set_object_type' ), 10, 4 );
\add_filter( 'wp_rest_cache/is_single_item', array( self::class, 'set_is_single_item' ), 10, 3 );
\add_action( 'transition_post_status', array( self::class, 'transition_post_status' ), 10, 3 );
\add_action( 'transition_comment_status', array( self::class, 'transition_comment_status' ), 10, 3 );
}

/**
* Add ActivityPub endpoints to the list of allowed endpoints.
*
* @param array $endpoints List of allowed endpoints.
*
* @return array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return array
* @return array Filtered list of allowed endpoints.

*/
public static function add_activitypub_endpoints( $endpoints ) {
$endpoints[ ACTIVITYPUB_REST_NAMESPACE ] = array( 'actors', 'collections', 'comments', 'interactions', 'nodeinfo', 'posts', 'users' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't the namespace simply activitypub or activitypub/1.0?


return $endpoints;
}

/**
* Set whether the cache represents a single item.
*
* Always return false for ActivityPub endpoints, since cache entries cannot be flushed otherwise.
*
* @param bool $is_single Whether the current cache represents a single item.
* @param mixed $data Data to cache.
* @param string $uri Request URI.
* @return bool Whether the cache represents a single item
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return bool Whether the cache represents a single item
*
* @return bool Whether the cache represents a single item

*/
public static function set_is_single_item( $is_single, $data, $uri ) {
if ( self::is_activitypub_endpoint( $uri ) ) {
return false;
}

return $is_single;
}

/**
* Set object type for ActivityPub.
*
* @param string $object_type Object type.
* @param string $cache_key Object key.
* @param mixed $data Data to cache.
* @param string $uri Request URI.
* @return string Updated object type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return string Updated object type
*
* @return string Updated object type

*/
public static function set_object_type( $object_type, $cache_key, $data, $uri ) {
if ( self::is_activitypub_endpoint( $uri ) ) {
return 'ActivityPub';
}

return $object_type;
}

/**
* Reset cache by transition post status.
*
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param \WP_Post $post Post object.
*/
public static function transition_post_status( $new_status, $old_status, $post ) {
if ( 'publish' !== $new_status && 'publish' !== $old_status ) {
return;
}

$post_types = (array) \get_option( 'activitypub_support_post_types', array() );
$post_types[] = Outbox::POST_TYPE;

if ( ! \in_array( $post->post_type, $post_types, true ) ) {
return;
}

Caching::get_instance()->delete_object_type_caches( 'ActivityPub' );
}

/**
* Reset cache by transition comment status.
*
* @param string $new_status The new comment status.
* @param string $old_status The old comment status.
* @param \WP_Comment $comment Comment object.
*/
public static function transition_comment_status( $new_status, $old_status, $comment ) {
if ( 'approved' !== $new_status && 'approved' !== $old_status ) {
return;
}

$comment_types = Comment::get_comment_type_slugs();
$comment_types[] = 'comment';

if ( ! \in_array( $comment->comment_type ?: 'comment', $comment_types, true ) ) { // phpcs:ignore Universal.Operators.DisallowShortTernary
return;
}

Caching::get_instance()->delete_object_type_caches( 'ActivityPub' );
}

/**
* Test, whether the current endpoint is an ActivityPub endpoint.
*
* @param string $uri URI to test.
* @return bool Whether the current endpoint is an ActivityPub endpoint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return bool Whether the current endpoint is an ActivityPub endpoint
*
* @return bool Whether the current endpoint is an ActivityPub endpoint

*/
private static function is_activitypub_endpoint( $uri ) {
$search = '/' . ACTIVITYPUB_REST_NAMESPACE . '/';

return \str_contains( $uri, $search ) || \str_contains( $uri, 'rest_route=' . \rawurlencode( $search ) );
}
}
4 changes: 4 additions & 0 deletions integration/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ function ( $transformer, $data, $object_class ) {
WPML::init();
}

if ( class_exists( 'WP_Rest_Cache_Plugin\Includes\Plugin' ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( class_exists( 'WP_Rest_Cache_Plugin\Includes\Plugin' ) ) {
if ( \class_exists( 'WP_Rest_Cache_Plugin\Includes\Plugin' ) ) {

WP_Rest_Cache::init();
}

/**
* Load the Surge integration.
*
Expand Down
Loading