0 ) {
$speedpage_act_services = true;
} else {
$speedpage_act_services = false;
}
}
define( 'SPEEDPAGE_ACTIVATION_SERVICE', $speedpage_act_services );
}
if ( SPEEDPAGE_ACTIVATION_SERVICE ) {
require_once plugin_dir_path( __FILE__ ) . 'includes/SpeedPageExcludeJsFromDelay.php';
}
const SPEEDPAGE_INCOMPATIBLE_PLUGINS = [
'w3-total-cache/w3-total-cache.php',
'wp-super-cache/wp-cache.php',
'tenweb-speed-optimizer/tenweb_speed_optimizer.php',
'wp-rocket/wp-rocket.php',
'rocket-footer-js/rocket-footer-js.php',
'autoptimize/autoptimize.php',
'perfmatters/perfmatters.php',
'wp-fastest-cache/wpFastestCache.php',
'wp-optimize/wp-optimize.php',
'wp-asset-clean-up/wpacu.php',
'rocket-lazy-load/rocket-lazy-load.php',
'hummingbird-performance/wp-hummingbird.php',
'wp-hummingbird/wp-hummingbird.php',
'flying-scripts/flying-scripts.php',
'async-javascript/async-javascript.php',
'nitropack/main.php',
'psn-pagespeed-ninja/pagespeedninja.php',
'swift-performance-lite/performance.php',
'swift-performance/performance.php',
'fast-velocity-minify/fvm.php',
'wp-performance-score-booster/wp-performance-score-booster.php',
'ezoic-integration/ezoic-integration.php',
'a3-lazy-load/a3-lazy-load.php',
'page-optimize/page-optimize.php',
'wp-smushit/wp-smush.php',
'performance-lab/load.php',
'airlift/airlift.php',
'flying-press/flying-press.php',
];
require_once SPEEDPAGE_PATH . 'includes/functions.php';
class speedpage {
public $cache_dir;
public $connect;
public $admin_bar;
private $speedpage_buffer_level = 0;
public $data_js = [];
public $speedpage_js_list_excluded = [];
public $speedpage_js_list = [];
private $exclude_blob_list = [
'adsbygoogle.js'
];
public function __construct() {
add_action( 'wp', [ $this, 'advanced_cache' ], 0 );
if ( ! SPEEDPAGE_GET_QUERY_SPEEDPAGE ) {
$this->cache_dir = WP_CONTENT_DIR . '/cache/speedpage/';
add_action( 'wp_body_open', function () {
if ( SPEEDPAGE_ACTIVATION_SERVICE ) {
echo '
';
for ( $i = 0; $i < 4; $i ++ ) {
echo '
';
}
echo '
';
}
} );
require_once plugin_dir_path( __FILE__ ) . 'includes/connect.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/admin-bar.php';
$this->connect = new SpeedPage_Connect( $this );
$this->admin_bar = new SpeedPage_Admin_Bar( $this );
add_action( 'admin_init', [ $this->connect, 'handle_connection' ] );
add_action( 'rest_api_init', [ $this, 'register_routes' ] );
add_action( 'rest_api_init', [ $this, 'register_routes_cache_page' ] );
add_action( 'rest_api_init', [ $this, 'receive' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'speedp_enqueue_admin_assets' ] );
#link to Settings | Docs
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), [ $this, 'add_plugin_links' ] );
# class admin-bar
add_action( 'admin_menu', [ $this->admin_bar, 'admin_menu' ] );
add_action( 'admin_bar_menu', [ $this->admin_bar, 'speedpage_add_admin_bar_button' ], 100 );
add_action( 'admin_init', [ $this->admin_bar, 'register_settings' ] );
if ( is_admin() ) {
// Hook sur l'enqueue d'administration de WordPress
add_action( 'admin_enqueue_scripts', [ $this, 'speedp_enqueue_dashboard_scripts' ] );
add_action( 'wp_ajax_speedpage_verify_connect', 'speedpage_verify_connect_ajax' );
add_action( 'wp_ajax_speedpage_score_checker', 'speedpage_score_checker_ajax' );
add_action( 'wp_ajax_speedpage_before_score_checker', 'speedpage_before_score_checker_ajax' );
add_action( 'wp_ajax_speedpage_deactivate_plugins', 'speedpage_deactivate_plugin_ajax' );
add_action( 'wp_ajax_speedpage_allow_api_synchro', 'speedpage_allow_api_synchro' );
add_action( 'wp_ajax_speedpage_realtime', 'speedpage_realtime_ajax' );
add_action( 'wp_ajax_speedpage_recive_data_all_pages', 'speedpage_recive_data_all_pages_ajax' );
// add_action( 'wp_ajax_speedpage_realtime_stockage', 'speedpage_realtime_stockage_ajax' );
if ( ! file_exists( $this->cache_dir ) ) {
wp_mkdir_p( $this->cache_dir );
}
if ( ! file_exists( $this->cache_dir . 'index.php' ) ) {
file_put_contents( $this->cache_dir . 'index.php', 'config = require plugin_dir_path( __FILE__ ) . 'includes/config.php';
add_filter( 'style_loader_tag', [ $this, 'filter_style_loader_tag' ], 10, 4 );#css deffer
add_action( 'wp_head', [ $this, 'critical_css' ], 0 );#css
add_action( 'template_redirect', array( $this, 'start_buffer' ), 0 );#script
}
}
}
#inject css to admin
public function speedp_enqueue_admin_assets( $hook ) {
if ( 'toplevel_page_speedpage_settings_page' !== $hook ) {
return;
}
wp_enqueue_style( 'speedpage-admin-style', SPEEDPAGE_URL . '/assets/css/speedpage.min.css', [], SPEEDPAGE_VERSION );
wp_enqueue_script( 'speedpage-admin', SPEEDPAGE_URL . '/assets/js/speedpage-admin.js', [ 'jquery' ], SPEEDPAGE_VERSION, true );
// Localize AFTER enqueue_script
wp_localize_script( 'speedpage-admin', 'SpeedPageData', [
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'speedpage_nonce' ),
] );
}
public function add_plugin_links( $links ) {
$settings_link = 'Settings';
$docs_link = 'Docs';
array_unshift( $links, $settings_link, $docs_link );
return $links;
}
public function register_routes_cache_page() {
register_rest_route( 'speedpage/v1', '/cache-page', [
'methods' => 'POST',
'callback' => [ $this, 'save_cache_page' ],
'permission_callback' => [ $this, 'verify_rest_api_credentials' ]
] );
}
public function save_cache_page( $request ) {
try {
$page_id = sanitize_text_field( $request['page_id'] );
$api_key = sanitize_text_field( $request['api_key'] );
$api_secret = sanitize_text_field( $request['api_secret'] );
$html = $request->get_body();
$site_id = get_option( 'speedpage_site_id' );
$site_secret = get_option( 'speedpage_site_secret' );
// 🔐 Vérification API
if ( $site_id !== $api_key || $site_secret !== $api_secret ) {
throw new Exception( 'Invalid API credentials' );
}
$css_dir = $this->cache_dir . 'pages_cache/';
// 📁 Create Folder
if ( ! file_exists( $css_dir ) ) {
if ( ! wp_mkdir_p( $css_dir ) ) {
throw new Exception( 'Unable to create PAGE CACHE directory' );
}
}
// 🔒 Protection index
if ( ! file_exists( $css_dir . 'index.php' ) ) {
if ( file_put_contents( $css_dir . 'index.php', ' 'success',
], 200 );
} catch ( Exception $e ) {
return new WP_REST_Response( [
'status' => 'error',
'message' => $e->getMessage(),
], 500 );
}
}
public function register_routes() {
register_rest_route( 'speedpage/v1', '/critical-css', [
'methods' => 'POST',
'callback' => [ $this, 'save_critical_css' ],
'permission_callback' => [ $this, 'verify_rest_api_credentials' ]
] );
}
public function verify_rest_api_credentials( $request ) {
if ( current_user_can( 'manage_options' ) ) {
return true;
}
$api_key = sanitize_text_field( $request->get_param( 'api_key' ) ?? '' );
$api_secret = sanitize_text_field( $request->get_param( 'api_secret' ) ?? '' );
$saved_id = get_option( 'speedpage_site_id' );
$saved_secret = get_option( 'speedpage_site_secret' );
if ( empty( $saved_id ) || empty( $saved_secret ) || $api_key !== $saved_id || $api_secret !== $saved_secret ) {
return new WP_Error(
'rest_forbidden',
__( 'Unauthorized request.', 'speedpage' ),
array( 'status' => 403 )
);
}
return true;
}
public function save_critical_css( $request ) {
try {
// Validate page ID strictly as a positive integer.
$raw_page_id = $request->get_param( 'page_id' );
if (
! is_scalar( $raw_page_id ) ||
filter_var(
(string) $raw_page_id,
FILTER_VALIDATE_INT,
array( 'options' => array( 'min_range' => 1 ) )
) === false
) {
return new WP_REST_Response(
array(
'status' => 'error',
'message' => 'Invalid page ID',
),
400
);
}
$page_id = (int) $raw_page_id;
$api_key = sanitize_text_field( $request->get_param( 'api_key' ) );
$api_secret = sanitize_text_field( $request->get_param( 'api_secret' ) );
$css = $request->get_body();
$site_id = get_option( 'speedpage_site_id' );
$site_secret = get_option( 'speedpage_site_secret' );
// Verify API credentials.
if (
! is_string( $site_id ) ||
! is_string( $site_secret ) ||
! hash_equals( $site_id, $api_key ) ||
! hash_equals( $site_secret, $api_secret )
) {
return new WP_REST_Response(
array(
'status' => 'error',
'message' => 'Invalid API credentials',
),
403
);
}
// Ensure the cache directory is configured.
if ( empty( $this->cache_dir ) ) {
throw new Exception( 'CSS cache directory is not configured' );
}
$css_dir = trailingslashit( $this->cache_dir ) . 'css/';
// Create the CSS directory if necessary.
if ( ! is_dir( $css_dir ) ) {
if ( ! wp_mkdir_p( $css_dir ) ) {
throw new Exception( 'Unable to create CSS directory' );
}
}
// Resolve the canonical CSS directory.
$real_css_dir = realpath( $css_dir );
if ( false === $real_css_dir || ! is_dir( $real_css_dir ) ) {
throw new Exception( 'Invalid CSS directory' );
}
// Create an index file to prevent directory listing.
$index_file = trailingslashit( $real_css_dir ) . 'index.php';
if ( ! file_exists( $index_file ) ) {
if (
file_put_contents(
$index_file,
' 'success', ), 200 );
} catch ( Exception $e ) {
return new WP_REST_Response(
array(
'status' => 'error',
'message' => $e->getMessage(),
),
500
);
}
}
public function receive() {
register_rest_route( 'speedpage/v1', '/receive', [
'methods' => 'POST',
'callback' => [ $this, 'receive_data' ],
'permission_callback' => [ $this, 'verify_rest_api_credentials' ]
] );
}
public function receive_data( $request ) {
$data = $request->get_json_params();
$key = $data['X-API-KEY'] ?? null;
$secret = $data['X-API-SECRET'] ?? null;
$type = $data['type'] ?? null;
if ( get_option( "speedpage_site_id" ) == $key && $secret == get_option( "speedpage_site_secret" ) && $type ) {
if ( $type == "score_before" ) {
$is_front_page = $data["data"]["is_front_page"];
if ( $is_front_page == 1 ) {
$stockage = get_option( 'speedpage_stockage' );
$stockage['front_page']['before'] = $data["data"]["result"];
update_option( "speedpage_stockage", $stockage );
}
// else {
$stockage = get_option( 'speedpage_stockage' );
$stockage['pages'][ $data["data"]["page_id"] ]['before'] = $data["data"]["result"];
$stockage["subscription_status"] = $data["data"]["subscription_status"];
$stockage["subscription_start"] = $data["data"]["subscription_start"];
$stockage["subscription_expiry"] = $data["data"]["subscription_expiry"];
update_option( "speedpage_stockage", $stockage );
// }
wp_send_json_success( $data );
}
if ( $type == "score_after" ) {
$is_front_page = $data["data"]["is_front_page"];
if ( $is_front_page == 1 ) {
$stockage = get_option( 'speedpage_stockage' );
$stockage['front_page']['after'] = $data["data"]["result"];
update_option( "speedpage_stockage", $stockage );
}
// else {
$stockage = get_option( 'speedpage_stockage' );
$stockage['pages'][ $data["data"]["page_id"] ]['after'] = $data["data"]["result"];
$stockage["subscription_status"] = $data["data"]["subscription_status"];
$stockage["subscription_start"] = $data["data"]["subscription_start"];
$stockage["subscription_expiry"] = $data["data"]["subscription_expiry"];
update_option( "speedpage_stockage", $stockage );
// }
wp_send_json_success( $data );
}
$stockage['subscription_status'] = isset( $data['subscription_status'] ) ? $data['subscription_status'] : false;
$stockage['subscription_start'] = isset( $data['subscription_start'] ) ? $data['subscription_start'] : false;
$stockage['subscription_expiry'] = isset( $data['subscription_expiry'] ) ? $data['subscription_expiry'] : false;
if ( $type == "all_pages_data" ) {
$stockage = get_option( 'speedpage_stockage' );
foreach ( $data["data"] as $page ):
if ( $page["score_before"] ) {
$stockage['pages'][ $page["page_id"] ]['before'] = json_decode( $page["score_before"], true );
}
if ( $page["score_after"] ) {
$stockage['pages'][ $page["page_id"] ]['after'] = json_decode( $page["score_after"], true );
}
endforeach;
update_option( "speedpage_stockage", $stockage );
wp_send_json_success( get_option( 'speedpage_stockage' ) );
}
} else {
return new WP_Error( 'unauthorized', 'Invalid API key', [ 'status' => 403 ] );
}
return new WP_Error( 'error', 'In function receive_data', [ 'status' => 400 ] );
}
public function filter_style_loader_tag( $tag, $handle, $href, $media ) {
if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
return $tag;
}
if ( apply_filters( 'speedpage_defer_styles_disabled', false ) ) {
return $tag;
}
if ( empty( $href ) || stripos( $tag, ' clearInterval(interval), 5000); window.addEventListener("load", function () { const skeleton = document.querySelector(".speedpageFrontSkeleton"); if (skeleton) { skeleton.remove() } const style = document.createElement("style"); style.innerHTML = ` body::before { display: none !important; } `; document.head.appendChild(style); });';
wp_add_inline_script( 'speedp-front-core-skelton', $js_code );
$page_id = absint( get_the_ID() );
$file = $this->cache_dir . 'css/critical-' . $page_id . '.css';
if ( ! file_exists( $file ) || ! is_readable( $file ) ) {
return;
}
$css = file_get_contents( $file );
if ( false === $css || '' === trim( $css ) ) {
return;
}
/*
* Critical CSS is inserted inside a sequence from terminating
* the HTML