Skip to content
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

Closes #7242 3.19 - Preload Fonts - Data insertion part #7274

Merged
Merged
Show file tree
Hide file tree
Changes from 19 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
29 changes: 29 additions & 0 deletions inc/Engine/Common/PerformanceHints/Cron/CronTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);

namespace WP_Rocket\Engine\Common\PerformanceHints\Cron;

trait CronTrait {
/**
* Performance Hints Deletion interval filter.
*
* @param string $filter_name The filter name.
*
* @return object
*/
public function deletion_interval( string $filter_name ): object {
/**
* Filters the interval (in months) to determine when a performance data entry is considered 'old'.
* Old performance entries are eligible for deletion. By default, a performance entry is considered old if it hasn't been accessed in the last month.
*
* @param int $delete_interval The interval in months after which a performance data entry is considered old. Default is 1 month.
*/
$delete_interval = wpm_apply_filters_typed( 'integer', $filter_name, 1 );

if ( $delete_interval <= 0 ) {
return $this->queries;
}

return $this->queries->set_cleanup_interval( $delete_interval ); // @phpstan-ignore-line
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
use WP_Rocket\Dependencies\BerlinDB\Database\Query;

class AbstractQueries extends Query {
/**
* Cleanup interval.
*
* @var int
*/
public $cleanup_interval;

/**
* Table status.
*
Expand Down Expand Up @@ -163,4 +170,17 @@ public function get_rows_by_url( string $url ) {

return $query;
}

/**
* Set cleanup interval
*
* @param int $interval The interval duration, usually default to 1.
*
* @return object
*/
public function set_cleanup_interval( int $interval ): object {
$this->cleanup_interval = $interval;

return $this;
}
}
12 changes: 1 addition & 11 deletions inc/Engine/Media/AboveTheFold/Database/Queries/AboveTheFold.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,7 @@ public function delete_old_rows() {
return false;
}

/**
* Filters the interval (in months) to determine when an Above The Fold (ATF) entry is considered 'old'.
* Old ATF entries are eligible for deletion. By default, an ATF entry is considered old if it hasn't been accessed in the last month.
*
* @param int $delete_interval The interval in months after which an ATF entry is considered old. Default is 1 month.
*/
$delete_interval = (int) apply_filters( 'rocket_atf_cleanup_interval', 1 );

if ( $delete_interval <= 0 ) {
return false;
}
$delete_interval = $this->cleanup_interval;

$prefixed_table_name = $db->prefix . $this->table_name;
$query = "DELETE FROM `$prefixed_table_name` WHERE status = 'failed' OR `last_accessed` <= date_sub(now(), interval $delete_interval month)";
Expand Down
5 changes: 4 additions & 1 deletion inc/Engine/Media/AboveTheFold/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace WP_Rocket\Engine\Media\AboveTheFold;

use WP_Rocket\Engine\Common\PerformanceHints\Cron\CronTrait;
use WP_Rocket\Engine\Common\PerformanceHints\FactoryInterface;
use WP_Rocket\Engine\Common\PerformanceHints\AJAX\ControllerInterface as AjaxControllerInterface;
use WP_Rocket\Engine\Common\PerformanceHints\Frontend\ControllerInterface as FrontendControllerInterface;
Expand All @@ -12,6 +13,7 @@
use WP_Rocket\Engine\Common\Context\ContextInterface;

class Factory implements FactoryInterface {
use CronTrait;

/**
* Ajax Controller instance.
Expand Down Expand Up @@ -98,7 +100,8 @@ public function table(): TableInterface {
* @return QueriesInterface
*/
public function queries(): QueriesInterface {
return $this->queries;
// Defines the interval for deletion and returns Queries object.
return $this->deletion_interval( 'rocket_atf_cleanup_interval' );
}

/**
Expand Down
146 changes: 146 additions & 0 deletions inc/Engine/Media/PreloadFonts/AJAX/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);

namespace WP_Rocket\Engine\Media\PreloadFonts\AJAX;

use WP_Rocket\Engine\Common\Context\ContextInterface;
use WP_Rocket\Engine\Common\PerformanceHints\AJAX\AJAXControllerTrait;
use WP_Rocket\Engine\Common\PerformanceHints\AJAX\ControllerInterface;
use WP_Rocket\Engine\Media\PreloadFonts\Database\Queries\PreloadFonts as PreloadFontsQuery;
use WP_Rocket\Engine\Optimization\UrlTrait;

class Controller implements ControllerInterface {
use AJAXControllerTrait;

/**
* PLFQuery instance
*
* @var PreloadFontsQuery
*/
private $query;

/**
* PreloadFonts Context.
*
* @var ContextInterface
*/
protected $context;

/**
* Constructor
*
* @param PreloadFontsQuery $query PLFQuery instance.
* @param ContextInterface $context Context interface.
*/
public function __construct( PreloadFontsQuery $query, ContextInterface $context ) {
$this->query = $query;
$this->context = $context;
}


/**
* Add Preload fonts data to the database
*
* @return array
*/
public function add_data(): array {
check_ajax_referer( 'rocket_beacon', 'rocket_beacon_nonce' );
$payload = [];

if ( ! $this->context->is_allowed() ) {
$payload['preload_fonts'] = 'not allowed';

return $payload;
}

$url = isset( $_POST['url'] ) ? untrailingslashit( esc_url_raw( wp_unslash( $_POST['url'] ) ) ) : '';
$is_mobile = isset( $_POST['is_mobile'] ) ? filter_var( wp_unslash( $_POST['is_mobile'] ), FILTER_VALIDATE_BOOLEAN ) : false;
$results = isset( $_POST['results'] ) ? json_decode( wp_unslash( $_POST['results'] ) ) : (object) [ 'preload_fonts' => [] ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$fonts = $results->preload_fonts ?? [];

$preload_fonts = [];

/**
* Filters the maximum number of fonts being saved into the database.
*
* @param int $max_number Maximum number to allow.
* @param string $url Current page url.
* @param string[]|array $hashes Current list of preload fonts.
*/
$max_preload_fonts_number = wpm_apply_filters_typed( 'integer', 'rocket_preload_fonts_number', 20, $url, $fonts );
if ( 0 >= $max_preload_fonts_number ) {
$max_preload_fonts_number = 1;
}

foreach ( (array) $fonts as $index => $font ) {
$preload_fonts[ $index ] = sanitize_text_field( wp_unslash( $font ) );
--$max_preload_fonts_number;
}

$row = $this->query->get_row( $url, $is_mobile );
if ( ! empty( $row ) ) {
$payload['preload_fonts'] = 'item already in the database';

return $payload;
}

$status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : '';
list( $status_code, $status_message ) = $this->get_status_code_message( $status );

$item = [
'url' => $url,
'is_mobile' => $is_mobile,
'status' => $status_code,
'error_message' => $status_message,
'fonts' => wp_json_encode( $preload_fonts ),
'created_at' => current_time( 'mysql', true ),
'last_accessed' => current_time( 'mysql', true ),
];

$result = $this->query->add_item( $item );

if ( ! $result ) {
$payload['preload_fonts'] = 'error when adding the entry to the database';

return $payload;
}

$payload['preload_fonts'] = $item;

return $payload;
}

/**
* Checks if there is existing data for the current URL and device type from the beacon script.
*
* This method is called via AJAX. It checks if there is existing fonts data for the current URL and device type.
* If the data exists, it returns a JSON success response with true. If the data does not exist, it returns a JSON success response with false.
* If the context is not allowed, it returns a JSON error response with false.
*
* @return array
*/
public function check_data(): array {
$payload = [
'preload_fonts' => false,
];

check_ajax_referer( 'rocket_beacon', 'rocket_beacon_nonce' );

if ( ! $this->context->is_allowed() ) {
$payload['preload_fonts'] = true;

return $payload;
}

$is_mobile = isset( $_POST['is_mobile'] ) ? filter_var( wp_unslash( $_POST['is_mobile'] ), FILTER_VALIDATE_BOOLEAN ) : false;
$url = isset( $_POST['url'] ) ? untrailingslashit( esc_url_raw( wp_unslash( $_POST['url'] ) ) ) : '';

$row = $this->query->get_row( $url, $is_mobile );

if ( ! empty( $row ) ) {
$payload['preload_fonts'] = true;
}

return $payload;
}
}
39 changes: 39 additions & 0 deletions inc/Engine/Media/PreloadFonts/Context/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);

namespace WP_Rocket\Engine\Media\PreloadFonts\Context;

use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Engine\Common\Context\ContextInterface;

class Context implements ContextInterface {
/**
* Instance of the Option_Data class.
*
* @var Options_Data
*/
private $options;

/**
* Constructor.
*
* @param Options_Data $options Instance of the Option_Data class.
*/
public function __construct( Options_Data $options ) {
$this->options = $options;
}

/**
* Determine if the action is allowed.
*
* @param array $data Data to pass to the context.
* @return bool
*/
public function is_allowed( array $data = [] ): bool {
if ( $this->options->get( 'wp_rocket_no_licence' ) ) {
return false;
}

return (bool) $this->options->get( 'rocket_preload_fonts', 1 );
}
}
91 changes: 91 additions & 0 deletions inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

namespace WP_Rocket\Engine\Media\PreloadFonts\Database\Queries;

use WP_Rocket\Engine\Common\PerformanceHints\Database\Queries\AbstractQueries;
use WP_Rocket\Engine\Common\PerformanceHints\Database\Queries\QueriesInterface;
use WP_Rocket\Engine\Media\PreloadFonts\Database\Schema\PreloadFonts as PreloadFontsSchema;
use WP_Rocket\Engine\Media\PreloadFonts\Database\Rows\PreloadFonts as PreloadFontsRows;
class PreloadFonts extends AbstractQueries implements QueriesInterface {

/**
* Name of the database table to query.
*
* @var string
*/
protected $table_name = 'wpr_preload_fonts';

/**
* String used to alias the database table in MySQL statement.
*
* Keep this short, but descriptive. I.E. "tr" for term relationships.
*
* This is used to avoid collisions with JOINs.
*
* @var string
*/
protected $table_alias = 'wpr_plf';

/**
* Name of class used to setup the database schema.
*
* @var string
*/
protected $table_schema = PreloadFontsSchema::class;

/**
* Name for a single item.
*
* Use underscores between words. I.E. "term_relationship"
*
* This is used to automatically generate action hooks.
*
* @var string
*/
protected $item_name = 'preload_fonts';

/**
* Plural version for a group of items.
*
* Use underscores between words. I.E. "term_relationships"
*
* This is used to automatically generate action hooks.
*
* @var string
*/
protected $item_name_plural = 'preload_fonts';

/**
* Name of class used to turn IDs into first-class objects.
*
* This is used when looping through return values to guarantee their shape.
*
* @var mixed
*/
protected $item_shape = PreloadFontsRows::class;


/**
* Deletes old rows from the database.
*
* This method is used to delete rows from the database that have not been accessed in the last month.
*
* @return bool|int Returns a boolean or integer value. The exact return value depends on the implementation.
*/
public function delete_old_rows() {
// Get the database interface.
$db = $this->get_db();

// Early bailout if no database interface is available.
if ( ! $db ) {
return false;
}

$delete_interval = $this->cleanup_interval;

$prefixed_table_name = $db->prefix . $this->table_name;
$query = "DELETE FROM `$prefixed_table_name` WHERE status = 'failed' OR `last_accessed` <= date_sub(now(), interval $delete_interval month)";

return $db->query( $query );
}
}
Loading
Loading