Your IP : 18.224.60.132
<?php
/**
* The viewport image class.
*
* @since 4.7
*/
namespace LiteSpeed;
defined('WPINC') || exit();
class VPI extends Base
{
const LOG_TAG = '[VPI]';
const TYPE_GEN = 'gen';
const TYPE_CLEAR_Q = 'clear_q';
protected $_summary;
private $_queue;
/**
* Init
*
* @since 4.7
*/
public function __construct()
{
$this->_summary = self::get_summary();
}
/**
* The VPI content of the current page
*
* @since 4.7
*/
public function add_to_queue()
{
$is_mobile = $this->_separate_mobile();
global $wp;
$request_url = home_url($wp->request);
$ua = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
// Store it to prepare for cron
$this->_queue = $this->load_queue('vpi');
if (count($this->_queue) > 500) {
self::debug('Queue is full - 500');
return;
}
$home_id = get_option('page_for_posts');
if (!is_singular() && !($home_id > 0 && is_home())) {
self::debug('not single post ID');
return;
}
$post_id = is_home() ? $home_id : get_the_ID();
$queue_k = ($is_mobile ? 'mobile' : '') . ' ' . $request_url;
if (!empty($this->_queue[$queue_k])) {
self::debug('queue k existed ' . $queue_k);
return;
}
$this->_queue[$queue_k] = array(
'url' => apply_filters('litespeed_vpi_url', $request_url),
'post_id' => $post_id,
'user_agent' => substr($ua, 0, 200),
'is_mobile' => $this->_separate_mobile(),
); // Current UA will be used to request
$this->save_queue('vpi', $this->_queue);
self::debug('Added queue_vpi [url] ' . $queue_k . ' [UA] ' . $ua);
// Prepare cache tag for later purge
Tag::add('VPI.' . md5($queue_k));
return null;
}
/**
* Notify finished from server
* @since 4.7
*/
public function notify()
{
$post_data = \json_decode(file_get_contents('php://input'), true);
if (is_null($post_data)) {
$post_data = $_POST;
}
self::debug('notify() data', $post_data);
$this->_queue = $this->load_queue('vpi');
// Validate key
if (empty($post_data['domain_key']) || $post_data['domain_key'] !== md5($this->conf(self::O_API_KEY))) {
self::debug('❌ notify wrong key');
self::save_summary(array('notify_ts_err' => time()));
return Cloud::err('wrong_key');
}
list($post_data) = $this->cls('Cloud')->extract_msg($post_data, 'vpi');
$notified_data = $post_data['data'];
if (empty($notified_data) || !is_array($notified_data)) {
self::debug('❌ notify exit: no notified data');
return Cloud::err('no notified data');
}
// Check if its in queue or not
$valid_i = 0;
foreach ($notified_data as $v) {
if (empty($v['request_url'])) {
self::debug('❌ notify bypass: no request_url', $v);
continue;
}
if (empty($v['queue_k'])) {
self::debug('❌ notify bypass: no queue_k', $v);
continue;
}
// $queue_k = ( $is_mobile ? 'mobile' : '' ) . ' ' . $v[ 'request_url' ];
$queue_k = $v['queue_k'];
if (empty($this->_queue[$queue_k])) {
self::debug('❌ notify bypass: no this queue [q_k]' . $queue_k);
continue;
}
// Save data
if (!empty($v['data_vpi'])) {
$post_id = $this->_queue[$queue_k]['post_id'];
$name = !empty($v['is_mobile']) ? 'litespeed_vpi_list_mobile' : 'litespeed_vpi_list';
$urldecode = is_array($v['data_vpi']) ? array_map('urldecode', $v['data_vpi']) : urldecode($v['data_vpi']);
self::debug('save data_vpi', $urldecode);
$this->cls('Metabox')->save($post_id, $name, $urldecode);
$valid_i++;
}
unset($this->_queue[$queue_k]);
self::debug('notify data handled, unset queue [q_k] ' . $queue_k);
}
$this->save_queue('vpi', $this->_queue);
self::debug('notified');
return Cloud::ok(array('count' => $valid_i));
}
/**
* Cron
*
* @since 4.7
*/
public static function cron($continue = false)
{
$_instance = self::cls();
return $_instance->_cron_handler($continue);
}
/**
* Cron generation
*
* @since 4.7
*/
private function _cron_handler($continue = false)
{
self::debug('cron start');
$this->_queue = $this->load_queue('vpi');
if (empty($this->_queue)) {
return;
}
// For cron, need to check request interval too
if (!$continue) {
if (!empty($this->_summary['curr_request_vpi']) && time() - $this->_summary['curr_request_vpi'] < 300 && !$this->conf(self::O_DEBUG)) {
self::debug('Last request not done');
return;
}
}
$i = 0;
foreach ($this->_queue as $k => $v) {
if (!empty($v['_status'])) {
continue;
}
self::debug('cron job [tag] ' . $k . ' [url] ' . $v['url'] . ($v['is_mobile'] ? ' 📱 ' : '') . ' [UA] ' . $v['user_agent']);
$i++;
$res = $this->_send_req($v['url'], $k, $v['user_agent'], $v['is_mobile']);
if (!$res) {
// Status is wrong, drop this this->_queue
$this->_queue = $this->load_queue('vpi');
unset($this->_queue[$k]);
$this->save_queue('vpi', $this->_queue);
if (!$continue) {
return;
}
// if ( $i > 3 ) {
GUI::print_loading(count($this->_queue), 'VPI');
return Router::self_redirect(Router::ACTION_VPI, self::TYPE_GEN);
// }
continue;
}
// Exit queue if out of quota
if ($res === 'out_of_quota') {
return;
}
$this->_queue = $this->load_queue('vpi');
$this->_queue[$k]['_status'] = 'requested';
$this->save_queue('vpi', $this->_queue);
self::debug('Saved to queue [k] ' . $k);
// only request first one
if (!$continue) {
return;
}
// if ( $i > 3 ) {
GUI::print_loading(count($this->_queue), 'VPI');
return Router::self_redirect(Router::ACTION_VPI, self::TYPE_GEN);
// }
}
}
/**
* Send to QC API to generate VPI
*
* @since 4.7
* @access private
*/
private function _send_req($request_url, $queue_k, $user_agent, $is_mobile)
{
$svc = Cloud::SVC_VPI;
// Check if has credit to push or not
$err = false;
$allowance = $this->cls('Cloud')->allowance($svc, $err);
if (!$allowance) {
self::debug('❌ No credit: ' . $err);
$err && Admin_Display::error(Error::msg($err));
return 'out_of_quota';
}
set_time_limit(120);
// Update css request status
self::save_summary(array('curr_request_vpi' => time()), true);
// Gather guest HTML to send
$html = $this->cls('CSS')->prepare_html($request_url, $user_agent);
if (!$html) {
return false;
}
// Parse HTML to gather all CSS content before requesting
$css = false;
list($css, $html) = $this->cls('CSS')->prepare_css($html);
if (!$css) {
self::debug('❌ No css');
return false;
}
$data = array(
'url' => $request_url,
'queue_k' => $queue_k,
'user_agent' => $user_agent,
'is_mobile' => $is_mobile ? 1 : 0, // todo:compatible w/ tablet
'html' => $html,
'css' => $css,
);
self::debug('Generating: ', $data);
$json = Cloud::post($svc, $data, 30);
if (!is_array($json)) {
return false;
}
// Unknown status, remove this line
if ($json['status'] != 'queued') {
return false;
}
// Save summary data
self::reload_summary();
$this->_summary['last_spent_vpi'] = time() - $this->_summary['curr_request_vpi'];
$this->_summary['last_request_vpi'] = $this->_summary['curr_request_vpi'];
$this->_summary['curr_request_vpi'] = 0;
self::save_summary();
return true;
}
/**
* Handle all request actions from main cls
*
* @since 4.7
*/
public function handler()
{
$type = Router::verify_type();
switch ($type) {
case self::TYPE_GEN:
self::cron(true);
break;
case self::TYPE_CLEAR_Q:
$this->clear_q('vpi');
break;
default:
break;
}
Admin::redirect();
}
}