<?php
/**
* Critical CSS base file (initializes all ccss files).
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class autoptimizeCriticalCSSBase {
/**
* Main plugin filepath.
* Used for activation/deactivation/uninstall hooks.
*
* @var string
*/
protected $filepath = null;
/**
* Critical CSS options
*
* @var array
*/
protected $_options = null;
/**
* Core object
*
* @var object
*/
protected $_core = null;
/**
* Cron object
*
* @var object
*/
protected $_cron = null;
/**
* Settings object
*
* @var object
*/
protected $_settings = null;
/**
* Enqueue object
*
* @var object
*/
protected $_enqueue = null;
public function __construct()
{
// define constant, but only once.
if ( ! defined( 'AO_CCSS_DIR' ) ) {
// Define a constant with the directory to store critical CSS in.
if ( is_multisite() ) {
$blog_id = get_current_blog_id();
define( 'AO_CCSS_DIR', WP_CONTENT_DIR . '/uploads/ao_ccss/' . $blog_id . '/' );
} else {
define( 'AO_CCSS_DIR', WP_CONTENT_DIR . '/uploads/ao_ccss/' );
}
}
if ( ! defined( 'AO_CCSS_VER' ) ) {
// Define plugin version.
define( 'AO_CCSS_VER', 'AO_' . AUTOPTIMIZE_PLUGIN_VERSION );
// Define constants for criticalcss.com base path and API endpoints.
// fixme: AO_CCSS_URL should be read from the autoptimize availability json stored as option.
define( 'AO_CCSS_URL', 'https://criticalcss.com' );
define( 'AO_CCSS_API', apply_filters( 'autoptimize_filter_ccss_service_url', AO_CCSS_URL . '/api/premium/' ) );
define( 'AO_CCSS_SLEEP', 10 );
}
// Define support files locations, in case they are not already defined.
if ( ! defined( 'AO_CCSS_LOCK' ) ) {
define( 'AO_CCSS_LOCK', AO_CCSS_DIR . 'queue.lock' );
}
if ( ! defined( 'AO_CCSS_LOG' ) ) {
define( 'AO_CCSS_LOG', AO_CCSS_DIR . 'queuelog.html' );
}
if ( ! defined( 'AO_CCSS_DEBUG' ) ) {
define( 'AO_CCSS_DEBUG', AO_CCSS_DIR . 'queue.json' );
}
$this->filepath = __FILE__;
// Add keychecker action for scheduled use.
add_action( 'ao_ccss_keychecker', array( $this, 'ao_ccss_check_key' ) );
}
public function setup() {
// check if we need to upgrade.
$this->check_upgrade();
// add/ remove scheduled jobs.
if ( $this->is_api_active() ) {
// make sure the 10 minutes cron schedule is added.
add_filter( 'cron_schedules', array( $this, 'ao_ccss_interval' ) );
if ( ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
// make sure ao_ccss_queue is scheduled OK if an API key is active.
wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
}
if ( ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
// and schedule maintenance job.
wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
}
if ( wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
// api is active now, no need to check key as it is checked by using the API.
wp_clear_scheduled_hook( 'ao_ccss_keychecker' );
}
} else {
if ( wp_next_scheduled( 'ao_ccss_queue' ) ) {
wp_clear_scheduled_hook( 'ao_ccss_queue' );
}
if ( wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
wp_clear_scheduled_hook( 'ao_ccss_maintenance' );
}
// add keychecker logic if api is not active but we have a key so maybe this is a temporary issue, check if key is OK daily.
$ao_ccss_key = $this->get_option( 'key' );
if ( ! empty( $ao_ccss_key ) && ! wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
wp_schedule_event( time(), 'twicedaily', 'ao_ccss_keychecker' );
} else if ( empty( $ao_ccss_key ) && wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
// edge case: we had a inactive key that was checked daily, but it is now removed, so remove keychecker from schedule.
wp_clear_scheduled_hook( 'ao_ccss_keychecker' );
}
}
// check/ create AO_CCSS_DIR.
if ( ! file_exists( AO_CCSS_DIR ) ) {
$this->create_ao_ccss_dir();
}
}
public function load_requires() {
// Required libs, core is always needed.
$this->_core = new autoptimizeCriticalCSSCore();
if ( ( defined( 'WP_CLI' ) || defined( 'DOING_CRON' ) || is_admin() ) && $this->is_api_active() ) {
// cron only initiated when doing cron (or wp_cli or is_amdin) and when we have an active API key.
$this->_cron = new autoptimizeCriticalCSSCron();
}
if ( is_admin() ) {
$this->_settings = new autoptimizeCriticalCSSSettings();
} else if ( $this->is_api_active() ) {
// enqueuing only done when not wp-admin and when API is active.
$this->_enqueue = new autoptimizeCriticalCSSEnqueue();
}
}
/**
* Log a message via CCSS Core object
*
* @param string $msg Message to log.
* @param int $lvl Loglevel.
*
* @return empty
*/
public function log( $msg, $lvl ) {
return $this->_core->ao_ccss_log( $msg, $lvl );
}
/**
* Get viewport from CCSS Core object
*
* @return array
*/
public function viewport() {
return $this->_core->ao_ccss_viewport();
}
/**
* Check CCSS contents from Core object
*
* @param string $ccss Critical CSS to be checked.
*
* @return bool
*/
public function check_contents( $ccss ) {
return $this->_core->ao_ccss_check_contents( $ccss );
}
/**
* Get key status from Core object
*
* @param bool $render Indicates if key status is to be rendered.
*
* @return array
*/
public function key_status( $render ) {
return $this->_core->ao_ccss_key_status( $render );
}
/**
* Return valid types from core object
*
* @return array
*/
public function get_types() {
return $this->_core->get_types();
}
/**
* Run enqueue in CCSS Enqueue object
*
* @param string $hash Hash (default empty).
* @param string $path Path (default empty).
* @param string $type (default is_page).
*/
public function enqueue( $hash = '', $path = '', $type = 'is_page' ) {
// Enqueue is sometimes required on wp-admin requests, load it just-in-time.
if ( is_null( $this->_enqueue ) && $this->is_api_active() ) {
$this->_enqueue = new autoptimizeCriticalCSSEnqueue();
}
return $this->_enqueue->ao_ccss_enqueue( $hash, $path, $type );
}
/**
* Check auto-rules in CCSS Settings object
*/
public function has_autorules() {
return $this->_settings->ao_ccss_has_autorules();
}
/**
* Get a Critical CSS option
*
* @param string $name The option name.
*
* @return mixed
*/
public function get_option( $name ) {
if ( is_null( $this->_options ) ) {
$this->fetch_options();
}
if ( isset( $this->_options[ $name ] ) ) {
return $this->_options[ $name ];
}
return null;
}
public function flush_options() {
$this->_options = null;
}
protected function fetch_options() {
if ( ! is_null( $this->_options ) ) {
return $this->_options;
}
$this->_options = array(
'css_defer' => autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ),
'css_defer_inline' => autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer_inline' ),
'rules_raw' => get_option( 'autoptimize_ccss_rules', false ),
'additional' => get_option( 'autoptimize_ccss_additional' ),
'queue_raw' => get_option( 'autoptimize_ccss_queue', false ),
'viewport' => get_option( 'autoptimize_ccss_viewport', false ),
'finclude' => get_option( 'autoptimize_ccss_finclude', false ),
'rtimelimit' => get_option( 'autoptimize_ccss_rtimelimit', '30' ),
'noptimize' => get_option( 'autoptimize_ccss_noptimize', false ),
'debug' => get_option( 'autoptimize_ccss_debug', false ),
'key' => apply_filters( 'autoptimize_filter_ccss_key', get_option( 'autoptimize_ccss_key' ) ),
'keyst' => get_option( 'autoptimize_ccss_keyst' ),
'loggedin' => get_option( 'autoptimize_ccss_loggedin', '1' ),
'forcepath' => get_option( 'autoptimize_ccss_forcepath', '1' ),
'servicestatus' => get_option( 'autoptimize_service_availablity' ),
'deferjquery' => get_option( 'autoptimize_ccss_deferjquery', false ),
'domain' => get_option( 'autoptimize_ccss_domain' ),
'unloadccss' => get_option( 'autoptimize_ccss_unloadccss', false ),
);
if ( strpos( $this->_options['domain'], 'http' ) === false && strpos( $this->_options['domain'], 'uggc' ) === 0 ) {
$this->_options['domain'] = str_rot13( $this->_options['domain'] );
} elseif ( strpos( $this->_options['domain'], 'http' ) !== false ) {
// not rot13'ed yet, do so now (goal; avoid migration plugins change the bound domain).
update_option( 'autoptimize_ccss_domain', str_rot13( $this->_options['domain'] ) );
}
// Setup the rules array.
if ( empty( $this->_options['rules_raw'] ) ) {
$this->_options['rules'] = array(
'paths' => array(),
'types' => array(),
);
} else {
$this->_options['rules'] = json_decode( $this->_options['rules_raw'], true );
}
// Setup the queue array.
if ( empty( $this->_options['queue_raw'] ) ) {
$this->_options['queue'] = array();
} else {
$this->_options['queue'] = json_decode( $this->_options['queue_raw'], true );
}
// Override API key if constant is defined.
if ( defined( 'AUTOPTIMIZE_CRITICALCSS_API_KEY' ) ) {
$this->_options['key'] = AUTOPTIMIZE_CRITICALCSS_API_KEY;
}
return $this->_options;
}
public function on_upgrade() {
$key = $this->get_option( 'key' );
// Create the cache directory if it doesn't exist already.
if ( ! file_exists( AO_CCSS_DIR ) ) {
$this->create_ao_ccss_dir();
}
// Create a scheduled event for the queue.
if ( $this->is_api_active() && ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
}
// Create a scheduled event for log maintenance.
if ( $this->is_api_active() && ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
}
}
public function check_upgrade() {
$db_version = get_option( 'autoptimize_ccss_version', '' );
if ( AO_CCSS_VER !== $db_version ) {
// check schedules & re-schedule if needed.
$this->on_upgrade();
// and update db_version.
update_option( 'autoptimize_ccss_version', AO_CCSS_VER );
}
}
public function ao_ccss_interval( $schedules ) {
// Let interval be configurable.
if ( ! defined( 'AO_CCSS_DEBUG_INTERVAL' ) ) {
$intsec = 600;
$inttxt = '10 minutes';
} else {
$intsec = AO_CCSS_DEBUG_INTERVAL;
if ( $intsec >= 120 ) {
$inttxt = $intsec / 60 . ' minutes';
} else {
$inttxt = $intsec . ' second(s)';
}
$this->log( 'Using custom WP-Cron interval of ' . $inttxt, 3 );
}
// Attach interval to schedule.
$schedules['ao_ccss'] = array(
'interval' => $intsec,
// translators: the variable contains a string describing the insterval.
'display' => sprintf( esc_html__( 'Every %s (Autoptimize Crit. CSS)', 'autoptimize' ), $inttxt ),
);
return $schedules;
}
public function create_ao_ccss_dir() {
// Make sure dir to write ao_ccss exists and is writable.
if ( ! is_dir( AO_CCSS_DIR ) ) {
// TODO: use wp_mkdir_p() ?
$mkdirresp = @mkdir( AO_CCSS_DIR, 0775, true ); // @codingStandardsIgnoreLine
} else {
$mkdirresp = true;
}
// Make sure our index.html is there.
if ( ! is_file( AO_CCSS_DIR . 'index.html' ) ) {
$fileresp = file_put_contents( AO_CCSS_DIR . 'index.html', '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' );
} else {
$fileresp = true;
}
if ( true === $fileresp && true === $mkdirresp ) {
return true;
} else {
return false;
}
}
/**
* Helper function to determine if there is an active API key.
*
* @return bool
*/
public function is_api_active() {
// using options instead of more complex $this->key_status (which gave some dependancy issues ... ;-) ).
$ao_ccss_key = $this->get_option( 'key' );
$ao_ccss_keyst = $this->get_option( 'keyst' );
if ( ! empty( $ao_ccss_key ) && $ao_ccss_keyst && 2 == $ao_ccss_keyst ) {
return true;
}
return false;
}
/**
* Helper function to determine if a rule is MANUAL.
*
* @param array $rule Rule to check.
*
* @return bool
*/
public function is_rule_manual( $rule ) {
if ( is_array( $rule ) && false == $rule['hash'] && false != $rule['file'] ) {
return true;
}
return false;
}
/**
* Scheduled action to check an inactive key. Not part of autoptimizeCriticalCSSCron.php
* to allow us to only load the main cron logic if we have an active key to begin with.
*/
public function ao_ccss_check_key() {
$ao_ccss_key = $this->get_option( 'key' );
$_result = $this->_core->ao_ccss_key_validation( $ao_ccss_key );
$_resmsg = ( true === $_result ) ? 'ok' : 'nok';
$this->log( 'Inactive key checked, result was ' . $_resmsg, 3 );
}
}