Skip to content
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 .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
vendor

# VS Code
*.code-workspace
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# WP Fragment Cache Plugin


**Author:** [Mindsize](https://mindsize.me)<br>
**Version**: 1.1.0

WordPress plugin to leverage different methods of caching.

This is an abstraction of fragment caching methods for developers to use for integration into WordPress plugins and themes. ***This is not intended to be used as a normal plugin, as it requires developer integration.***

## Usage

Provide documentation on `$conditions` array usage. Note that the Object Cache class looks for an optional "expire" value.

## Examples

Usage examples.

25 changes: 8 additions & 17 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,17 @@
"email": "gabor@mindsize.me",
"homepage": "http://mindsize.me",
"role": "Developer"
},
{
"name": "John Regan",
"email": "john.regan@mindsize.com",
"homepage": "http://mindsize.com",
"role": "Developer"
}
],
"license": "GPL-2.0",
"autoload": {
"classmap": [
"src"
]
},
"require": {
"xrstf/composer-php52": "^1.0"
"classmap": ["src"]
},
"scripts": {
"post-install-cmd": [
"xrstf\\Composer52\\Generator::onPostInstallCmd"
],
"post-update-cmd": [
"xrstf\\Composer52\\Generator::onPostInstallCmd"
],
"post-autoload-dump": [
"xrstf\\Composer52\\Generator::onPostInstallCmd"
]
}
"require": {}
}
71 changes: 71 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Mindsize" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/squizlabs/PHP_CodeSniffer/master/phpcs.xsd">
<description>The Mindsize PHP coding standard.</description>

<exclude-pattern>*/node_modules/*</exclude-pattern>
<exclude-pattern>*/vendor/*</exclude-pattern>
<exclude-pattern>*.js</exclude-pattern>
<exclude-pattern>*.css</exclude-pattern>

<!-- Use the WordPress ruleset, with some customizations. -->
<rule ref="WordPress">
<!-- Allow short array syntax. -->
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found" />
<!-- Allow short ternary expressions -->
<exclude name="WordPress.PHP.DisallowShortTernary" />

<!-- Allow PSR4 Filenames -->
<exclude name="WordPress.Files.FileName.NotHyphenatedLowercase" />
<exclude name="WordPress.Files.FileName.InvalidClassFileName" />
</rule>

<!-- Use the WordPress-Extra ruleset. -->
<rule ref="WordPress-Extra" />

<!--
Mindsize specific customizations.
-->

<!-- We prefer short array syntax. -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax" />

<!-- Allow for common global prefixes used in Mindsize code. -->
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<properties>
<property name="prefixes" type="array" value="wp_starter_theme,ms_,_ms" />
</properties>

<!-- In practice, partials should be loaded outside of the global namespace with get_template_part(). -->
<exclude-pattern>themes(/vip)?/[^/]+/template-parts/</exclude-pattern>
<exclude-pattern>vendor/</exclude-pattern>
</rule>

<!-- Keep common boilerplate code from triggering an error. -->
<rule ref="Squiz.Commenting.FileComment.SpacingAfterComment">
<!--
Downgrade while so many PHP files are empty to start with.
The empty file makes it difficult to comply with this rule
and the trailing newline rule at the same time.
-->
<type>warning</type>

<!--
Once your PHP files have been filled in, you might be able
to start checking for compliance again with an
exclude-pattern like these:

<exclude-pattern>themes(/vip)?/[^/]+/[^/]+\.php$</exclude-pattern>
<exclude-pattern>themes(/vip)?/[^/]+/template-parts/*</exclude-pattern>
-->
</rule>

<!-- Encourage having only one class/interface/trait per file. -->
<rule ref="Generic.Files.OneObjectStructurePerFile">
<!--
Increase severity. If it becomes a problem for this sniff to trigger an
error, then we might want to provide feedback to the WPCS maintainers.
See https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/pull/1111.
-->
<type>error</type>
</rule>
</ruleset>
132 changes: 64 additions & 68 deletions src/wp-fragment-cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,17 @@
/**
* WP Fragment Cache Framework
*
* This source file is subject to the GNU General Public License v3.0
* that is bundled with this package in the file license.txt.
* It is also available through the world-wide-web at this URL:
* http://www.gnu.org/licenses/gpl-2.0.html
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to info@mindsize.me so we can send you a copy immediately.
*
* @package Mindsize/WP_Fragment_Cache
* @author Mindsize
* @copyright Copyright (c) 2017, Mindsize, LLC.
* @copyright Copyright (c) 2017-2021, Mindsize, LLC.
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2.0
*/

if( ! defined( 'ABSPATH' ) ) {
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* If the class already exists, no need to redeclare it.
*/
if( class_exists( 'WP_Fragment_Cache' ) ) {
if ( class_exists( 'WP_Fragment_Cache' ) ) {
return;
}

Expand All @@ -44,106 +33,112 @@ abstract class WP_Fragment_Cache {
/**
* Abstracted method for classes to override and store their data.
*
* @param $data
*
* @return bool
* @param string $output Output string.
* @param array $conditions Array of conditions.
*/
abstract protected function set_cache_data( $output, $conditions );

/**
* Abstracted method for classes to override and get their data.
*
* @param $data
*
* @return bool
* @param array $conditions Array of conditions.
*/
abstract protected function get_cache_data( $conditions );

/**
* Abstracted method for classes to override and clear their cache
*
* @param $data
*
* @return bool
* Abstracted method for classes to override and clear their cache.
*/
abstract public function clear_cache();

/**
* Opening comment before cached content is output.
* Is stored within the cached content and is not generated on every load.
* Get the opening comment before cached content is output.
* This is stored within the cached content and is not generated on every load.
*/
protected function get_cache_start_comment() { return null; }
protected function get_cache_start_comment() {
return null;
}

/**
* Closing comment before cached content is output.
* Is stored within the cached content and is not generated on every load.
* Get the closing comment before cached content is output.
* This is stored within the cached content and is not generated on every load.
*/
protected function get_cache_close_comment() { return null; }
protected function get_cache_close_comment() {
return null;
}

/**
* Debug comment string for the cached data
* Get the debug comment string for the cached data.
*
* @param float $start The start time in UNIX format.
* @param float $end The end time in UNIX format.
*
* @return string
*/
protected function get_cache_debug_comment( $start, $end ) {
return sprintf( __( 'Took %f seconds to store cached content.', 'ms-wp-fragment-cache' ), $end - $start );
// translators: The number of seconds elapsed.
return sprintf( __( 'Took %f seconds to store cached content.', 'ms-wp-fragment-cache' ), floatval( $end ) - floatval( $start ) );
}

/**
* Output a HTML comment, if the comment is empty, no HTML is output.
* Output a HTML comment, if the comment is empty, no output is rendered.
*
* @param string $comment The code Comment.
*/
protected function output_comment( $comment ) {
if( ! empty( $comment ) ) {
printf( '<!-- %s -->', $comment );
if ( empty( $comment ) || ! is_string( $comment ) ) {
return;
}

// Escape as best we can.
printf( '<!-- %s -->', esc_html( $comment ) );
}

/**
* Actually cache the output of the passed callback, optionally outputting the result also. Allows for passing
* arguments for conditional caching.
*
* @param $callback
* @param bool $also_output
* @param array $args
* Note: Rendering the content must have some kind of escaping.
* Dangerous input will be served to multiple users as the cached content is presented.
*
* @return bool
* @link https://portswigger.net/web-security/web-cache-poisoning
*
* @param string|array $callback Callable callback function/method.
* @param array $conditions Optional. Array of Conditions.
* @param bool $render Optional. Render the cached HTML in addition to returning. Defaults to true.
* @param bool $refresh Optional. Refresh the cache. Defaults to false.
*
* @return mixed The cached content, else false.
*/
public function do( $callback, $conditions = array(), $also_output = true, $refresh = false ) {
/**
* If the callback passed is not callable, just bail here and false and a notice.
*/
if( ! is_callable( $callback ) ) {
trigger_error( __( 'Unable to call required callback function.', 'ms-wp-fragment-cache' ) );

public function do( $callback, $conditions = [], $render = true, $refresh = false ) {

// If the callback passed is not callable, just bail here and false and a notice.
if ( ! is_callable( $callback ) && $this->is_debug() ) {
// Ignore PHPCS as "is_debug" is verified.
// phpcs:disable WordPress.PHP.DevelopmentFunctions
trigger_error( esc_html_e( 'Unable to call required callback function.', 'ms-wp-fragment-cache' ) );
// phpcs:enable
return false;
}

$contents = $this->get_cache_data( $conditions );
if( true === $refresh || false === $contents ) {
/**
* Start saving all output into a buffer for capture later.
*/

if ( true === $refresh || false === $contents ) {
// Start saving all output into a buffer for capture later.
ob_start();

/**
* Output the starting HTML comment.
*/
// Output the starting HTML comment.
$this->output_comment( $this->get_cache_start_comment() );

/**
* Call the callback, pass the conditions to the callback as well.
*/
// Call the callback, pass the conditions to the callback as well.
$start = microtime( true );
call_user_func_array( $callback, $conditions );
$end = microtime( true );

/**
* Output the closing HTML comment.
*/
// Output the closing HTML comment.
$this->output_comment( $this->get_cache_close_comment() );

/**
* If we are debugging, then also output some debugging data.
*/
if( $this->is_debug() ) {
// If we are debugging, then also output some debugging data.
if ( $this->is_debug() ) {
$this->output_comment( $this->get_cache_debug_comment( $start, $end ) );
}

Expand All @@ -152,15 +147,16 @@ public function do( $callback, $conditions = array(), $also_output = true, $refr
$this->set_cache_data( $contents, $conditions );
}

if( true === $also_output ) {
// Allow optional rendering after caching.
if ( true === $render ) {
print( $contents );
}

return $contents;
}

/**
* Get the slug of the class
* Get the class slug.
*
* @return string
*/
Expand All @@ -180,11 +176,11 @@ public function is_debug() {
/**
* Use the class slug to define hooks dynamically.
*
* @param $name
* @param string $name The hook name.
*
* @return string
*/
protected function get_hook_name( $name ) {
return sanitize_title( sprintf( '%s_%s', $this->get_slug(), $name ) );
}
}
}
Loading