felixarntz/wp-shortcode-cache

View on GitHub
wp-shortcode-cache/class-wp-shortcode-cache-tag.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
/**
 * WP_Shortcode_Cache_Tag class
 *
 * @package WPShortcodeCache
 * @since 1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class representing a specific shortcode and its external cache data.
 *
 * @since 1.0.0
 */
class WP_Shortcode_Cache_Tag {
    /**
     * Shortcode name.
     *
     * @since 1.0.0
     * @access private
     * @var string
     */
    private $tag;

    /**
     * Registered callbacks for external data.
     *
     * @since 1.0.0
     * @access private
     * @var array
     */
    private $callbacks = array();

    /**
     * Registered globals and superglobals for external data.
     *
     * @since 1.0.0
     * @access private
     * @var array
     */
    private $globals = array();

    /**
     * Duration for which to cache this shortcode.
     *
     * @since 1.0.0
     * @access private
     * @var int
     */
    private $duration = 0;

    /**
     * Constructor.
     *
     * @since 1.0.0
     * @access public
     *
     * @see WP_Shortcode_Cache_Tag::register_external_data_values()
     *
     * @param string     $tag           Shortcode name.
     * @param array|null $external_data Optional. External data values to register. Default null.
     */
    public function __construct( $tag, $external_data = null ) {
        $this->tag = $tag;

        if ( is_array( $external_data ) ) {
            $this->register_external_data_values( $external_data );
        }
    }

    /**
     * Registers multiple external data values.
     *
     * This method is a shortcut for calling WP_Shortcode_Cache_Tag::register_external_data_value()
     * multiple times.
     *
     * @since 1.0.0
     * @access public
     *
     * @see WP_Shortcode_Cache_Tag::register_external_data_value()
     *
     * @param array $external_data Array of $identifier => $params pairs. Each $params
     *                             element can either be a string used as `name`, or for
     *                             more complex use-cases an array containing a `name` key,
     *                             and optionally `type` and `args` keys.
     * @return bool|WP_Error True on success, error object on failure.
     */
    public function register_external_data_values( $external_data ) {
        $error = new WP_Error();

        foreach ( $external_data as $identifier => $params ) {
            if ( is_string( $params ) ) {
                $result = register_external_data_value( $identifier, $params );
                if ( is_wp_error( $result ) ) {
                    $error->add( $result->get_error_code(), $result->get_error_message() );
                }
            } elseif ( ! isset( $params['name'] ) ) {
                /* translators: %s: shortcode name */
                $error->add( 'missing_external_data_value_name', __( 'The name argument is missing for external cache data registered for shortcode %s.', 'wp-shortcode-cache' ), esc_attr( $this->tag ) );
            } else {
                $result = register_external_data_value( $identifier, $params['name'], isset( $params['type'] ) ? $params['type'] : 'global', isset( $params['args'] ) ? $params['args'] : array() );
                if ( is_wp_error( $result ) ) {
                    $error->add( $result->get_error_code(), $result->get_error_message() );
                }
            }
        }

        if ( ! empty( $error->errors ) ) {
            return $error;
        }

        return true;
    }

    /**
     * Registers an external data value.
     *
     * @since 1.0.0
     * @access public
     *
     * @param string          $identifier Unique identifier for this external data value. This value is
     *                                    used as array key for external data. The name of an existing
     *                                    shortcode attribute may be passed so that this value acts as
     *                                    a fallback.
     * @param string|callable $name       Name of global key, or callback function if $type is 'callback'.
     * @param string          $type       Optional. Either 'callback', 'global', 'request', 'get', 'post'
     *                                    or 'session'. Default 'global'.
     * @param array           $args       Optional. Additional arguments passed to a callback. Default empty.
     * @return bool|WP_Error True on success, error object on failure.
     */
    public function register_external_data_value( $identifier, $name, $type = 'global', $args = array() ) {
        switch ( $type ) {
            case 'callback':
                $this->callbacks[ $identifier ] = array(
                    'name'         => $name,
                    'args'         => $args,
                );
                return true;
            case 'global':
            case 'request':
            case 'get':
            case 'post':
            case 'session':
                $this->globals[ $identifier ] = array(
                    'name'         => $name,
                    'type'         => $type,
                );
                return true;
        }

        /* translators: 1: type, 2: shortcode name */
        return new WP_Error( 'invalid_external_data_value_type', sprintf( __( '%s is not a valid type for external cache data registered for shortcode %2$s.', 'wp-shortcode-cache' ), esc_attr( $type ), esc_attr( $this->tag ) ) );
    }

    /**
     * Unregisters an external data value.
     *
     * @since 1.0.0
     * @access public
     *
     * @param string $identifier Unique identifier of the external data value to unregister.
     * @return bool|WP_Error True on success, error object on failure.
     */
    public function unregister_external_data_value( $identifier ) {
        if ( isset( $this->callbacks[ $identifier ] ) ) {
            unset( $this->callbacks[ $identifier ] );
            return true;
        }

        if ( isset( $this->globals[ $identifier ] ) ) {
            unset( $this->globals[ $identifier ] );
            return true;
        }

        return new WP_Error( 'invalid_external_data_value_identifier', sprintf( __( 'No external cache data value with identifier %1$s is registered for shortcode %2$s.', 'wp-shortcode-cache' ), esc_attr( $identifier ), esc_attr( $this->tag ) ) );
    }

    /**
     * Sets the duration for which this shortcode should be cached.
     *
     * @since 1.0.0
     * @access public
     *
     * @param int $duration Cache duration in seconds. Set to 0 for no expiration.
     * @return bool|WP_Error True on success, error object on failure.
     */
    public function set_cache_duration( $duration ) {
        $this->duration = absint( $duration );

        return true;
    }

    /**
     * Returns the duration for which this shortcode should be cached.
     *
     * @since 1.0.0
     * @access public
     *
     * @return int Cache duration in seconds, or 0 for no expiration.
     */
    public function get_cache_duration() {
        return $this->duration;
    }

    /**
     * Fills the regular shortcode attributes array with external data.
     *
     * The return value of this method is safe to be used as a set of data for
     * generating a unique cache key.
     *
     * The external data is fetched using the registered external data values.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $attr Shortcode attributes.
     * @return array Shortcode attributes including external data.
     */
    public function fill_external_data( $attr ) {
        foreach ( $this->callbacks as $identifier => $params ) {
            if ( isset( $attr[ $identifier ] ) ) {
                continue;
            }

            /* Special case for get_post() function. */
            if ( 'get_post' === $params['name'] && empty( $params['args'] ) ) {
                $attr = $this->fill_current_post( $attr, $identifier );
            } else {
                if ( is_callable( $params['name'] ) ) {
                    $attr[ $identifier ] = call_user_func_array( $params['name'], $params['args'] );
                }
            }
        }

        foreach ( $this->globals as $identifier => $params ) {
            if ( isset( $attr[ $identifier ] ) ) {
                continue;
            }

            /* Special case for global $post. */
            if ( 'global' === $params['type'] && 'post' === $params['name'] ) {
                $attr = $this->fill_current_post( $attr, $identifier );
            } else {
                switch ( $params['type'] ) {
                    case 'global':
                        if ( isset( $GLOBALS[ $params['name'] ] ) ) {
                            $attr[ $identifier ] = $GLOBALS[ $params['name'] ];
                        }
                        break;
                    case 'request':
                        if ( isset( $_REQUEST[ $params['name'] ] ) ) {
                            $attr[ $identifier ] = $_REQUEST[ $params['name'] ];
                        }
                        break;
                    case 'get':
                        if ( isset( $_GET[ $params['name'] ] ) ) {
                            $attr[ $identifier ] = $_GET[ $params['name'] ];
                        }
                        break;
                    case 'post':
                        if ( isset( $_POST[ $params['name'] ] ) ) {
                            $attr[ $identifier ] = $_POST[ $params['name'] ];
                        }
                        break;
                    case 'session':
                        if ( isset( $_SESSION[ $params['name'] ] ) ) {
                            $attr[ $identifier ] = $_SESSION[ $params['name'] ];
                        }
                        break;
                }
            }
        }

        return $attr;
    }

    /**
     * Fills the regular shortcode attributes array with the current post.
     *
     * This is a utility method to handle latest changes in that post automatically.
     *
     * @since 1.0.0
     * @access private
     *
     * @param array  $attr       Shortcode attributes.
     * @param string $identifier Unique identifier to the field the post should be stored in.
     * @return array Shortcode attributes including the current post data.
     */
    private function fill_current_post( $attr, $identifier ) {
        $post = get_post();

        if ( $post ) {
            $attr[ $identifier ] = $post->ID;
            $attr[ $identifier . '_last_changed' ] = $post->post_modified_gmt;
        }

        return $attr;
    }
}