Current File : /home/resuelf/www/wp-content/plugins/nitropack/nitropack-sdk/NitroPack/SDK/Api/Cache.php
<?php
namespace NitroPack\SDK\Api;

use \NitroPack\SDK\NitroPack;
use \NitroPack\SDK\ServiceDownException;
use \NitroPack\HttpClient\HttpClientMulti;
use \NitroPack\HttpClient\Exceptions\SocketReadTimedOutException;

class Cache extends SignedBase {
    protected $secret;

    public function __construct($siteId, $siteSecret) {
        parent::__construct($siteId, $siteSecret);
        $this->secret = $siteSecret;
    }

    public function get($url, $userAgent, $cookies, $isAjax, $layout = 'default', $remoteAddr = NULL, $referrer = NULL) {
        $this->isBacklogEnabled = false;
        $path = 'cache/get/' . $this->siteId . '/' . $layout;
        $remoteAddr = $remoteAddr ? $remoteAddr : NitroPack::getRemoteAddr();

        $headers = array(
            'X-Nitro-Url' => $url,
            'X-Nitro-Visitor-Addr' => $remoteAddr,
            'User-Agent' => $userAgent
        );

        $customCachePrefix = NitroPack::getCustomCachePrefix();
        if ($customCachePrefix) {
            $headers['X-Nitro-Cache-Prefix'] = $customCachePrefix;
        }

        if ($isAjax) {
            $headers['X-Nitro-Ajax'] = 1;
        }

        if ($referrer) {
            $headers['Referer'] = $referrer;
        }

        if ($this->isCacheWarmupRequest()) {
            $headers['X-Nitro-Priority'] = 'LOW';
        }

        if (!empty($this->nitropack->getConfig()->LoopbackRequests)) {
            $headers['X-Nitro-Loopback'] = '1';
        }

        $httpResponse = $this->makeRequest($path, $headers, $cookies);
        $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
        $body = $httpResponse->getBody();
        $response = new Response($status, $body);
        return $response;
    }

    public function getMulti($urls, $userAgent, $cookies, $isAjax, $layout = 'default', $remoteAddr = NULL, $referrer = NULL) {
        $this->isBacklogEnabled = false;
        $path = 'cache/get/' . $this->siteId . '/' . $layout;
        $remoteAddr = $remoteAddr ? $remoteAddr : NitroPack::getRemoteAddr();

        $headers = array(
            'X-Nitro-Visitor-Addr' => $remoteAddr,
            'User-Agent' => $userAgent
        );

        $customCachePrefix = NitroPack::getCustomCachePrefix();
        if ($customCachePrefix) {
            $headers['X-Nitro-Cache-Prefix'] = $customCachePrefix;
        }

        if ($isAjax) {
            $headers['X-Nitro-Ajax'] = 1;
        }

        if ($referrer) {
            $headers['Referer'] = $referrer;
        }

        if ($this->isCacheWarmupRequest()) {
            $headers['X-Nitro-Priority'] = 'LOW';
        }

        if (!empty($this->nitropack->getConfig()->LoopbackRequests)) {
            $headers['X-Nitro-Loopback'] = '1';
        }

        $chunkSize = min(5, count($urls));
        $cache = $this;
        $retries = new \SplObjectStorage();
        $httpMulti = new HttpClientMulti();
        
        $httpMulti->onSuccess(function($client) use ($path, &$urls, $httpMulti, $cache, $headers, $cookies) {
            if ($urls) {
                $_url = array_shift($urls);
                $headers['X-Nitro-Url'] = $_url;

                $httpClient = $cache->makeRequestAsync($path, $headers, $cookies);
                $httpMulti->push($httpClient);
            }
        });

        $httpMulti->onError(function($client, $exception) use ($httpMulti, $retries) {
            if ($exception instanceof SocketReadTimedOutException) {
                return;
            }

            if (!$retries->offsetExists($client)) {
                $clientRetries = 0;
            } else {
                $clientRetries = $retries->offsetGet($client);
            }

            if ($clientRetries < 5) {
                $retries->offsetSet($client, $clientRetries + 1);
                $client->replay();
                $httpMulti->push($client);
            }
        });

        while ($urls && count($httpMulti->getClients()) < $chunkSize) {
            $_url = array_shift($urls);
            $headers['X-Nitro-Url'] = $_url;

            try {
                $httpClient = $this->makeRequestAsync($path, $headers, $cookies);
            } catch (ServiceDownException $e) {
                continue;
            }

            $httpMulti->push($httpClient);
        }

        $res = $httpMulti->readAll(); // This blocks untill all requests have finished

        foreach ($res[1] as $failedRequest) {
            $exception = $failedRequest[1];

            if ($exception instanceof SocketReadTimedOutException) {
                continue; // Ignore read timeouts
            } else {
                throw $exception;
            }
        }

        $responses = [];
        foreach ($res[0] as $httpResponse) {
            $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
            if ($status !== ResponseStatus::OK) {
                $this->throwException($httpResponse, 'Error while getting cache: %s');
            }

            $url = !empty($httpResponse->request_headers['x-nitro-url']) ? $httpResponse->request_headers['x-nitro-url'] : null;
            if (!$url) {
                $this->throwException($httpResponse, 'Error while getting cache, url header missing: %s');
            }

            $body = $httpResponse->getBody();
            $responses[$url] = new Response($status, $body);
        }

        return $responses;
    }

    public function getLastPurge() {
        $this->isBacklogEnabled = false;
        $path = 'cache/getlastpurge/' . $this->siteId;

        $httpResponse = $this->makeRequest($path);
        $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
        switch ($status) {
        case ResponseStatus::OK:
            return json_decode($httpResponse->getBody(), true);
        default:
            $this->throwException($httpResponse, 'Error while getting information about the last cache purge: %s');
        }
    }

    public function purge($url = NULL, $pagecacheOnly = false, $reason = NULL, $lightPurge = false) {
        $this->isBacklogEnabled = true;
        $path = 'cache/purge/' . $this->siteId;

        if (is_array($url)) {
            $chunkSize = min(25, (int)ceil(count($url) * 0.2)); // 20% of the number of URLs up to 25 simultaneous requests
            $requests = array();
            $cache = $this;
            $retries = new \SplObjectStorage();
            $httpMulti = new HttpClientMulti();
            
            $httpMulti->onSuccess(function($client) use ($path, &$url, &$requests, $httpMulti, $cache, $reason, $lightPurge) {
                if ($client->getStatusCode() >= 500 && !empty($client->backlogEntry)) {
                    $this->addToBacklog($client->backlogEntry);
                    $this->nitropack && $this->nitropack->setHealthStatus(HealthStatus::UNDER_THE_WEATHER);
                }

                if ($url) {
                    $_url = array_shift($url);
                    $params = array();
                    $params["url"] = $_url;

                    if ($reason) {
                        $params["reason"] = $reason;
                    }
                    if ($lightPurge) {
                        $params["light_purge"] = $lightPurge;
                    }
                    $httpClient = $cache->makeRequestAsync($path, array(), array(), 'POST', $params);
                    $httpMulti->push($httpClient);
                    $requests[] = $httpClient;
                }
            });

            $httpMulti->onError(function($client, $exception) use ($httpMulti, $retries) {
                if ($exception instanceof SocketReadTimedOutException) {
                    if (!empty($client->backlogEntry)) {
                        $this->addToBacklog($client->backlogEntry);
                        $this->nitropack && $this->nitropack->setHealthStatus(HealthStatus::SICK);
                    }
                    return;
                }

                if (!$retries->offsetExists($client)) {
                    $clientRetries = 0;
                } else {
                    $clientRetries = $retries->offsetGet($client);
                }

                if ($clientRetries < 5) {
                    $retries->offsetSet($client, $clientRetries + 1);
                    $client->replay();
                    $httpMulti->push($client);
                } else {
                    if (!empty($client->backlogEntry)) {
                        $this->addToBacklog($client->backlogEntry);
                    }
                }
            });

            while ($url && count($httpMulti->getClients()) < $chunkSize) {
                $_url = array_shift($url);
                $params = array();
                $params["url"] = $_url;

                if ($reason) {
                    $params["reason"] = $reason;
                }
                if ($lightPurge) {
                    $params["light_purge"] = $lightPurge;
                }

                try {
                    $httpClient = $this->makeRequestAsync($path, array(), array(), 'POST', $params);
                } catch (ServiceDownException $e) {
                    continue;
                }

                $httpMulti->push($httpClient);
                $requests[] = $httpClient;
            }

            $res = $httpMulti->readAll(); // This blocks untill all requests have finished

            foreach ($res[1] as $failedRequest) {
                $exception = $failedRequest[1];

                if ($exception instanceof SocketReadTimedOutException) {
                    continue; // Ignore read timeouts
                } else {
                    throw $exception;
                }
            }

            foreach ($res[0] as $httpResponse) {
                $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
                if ($status !== ResponseStatus::OK) {
                    $this->throwException($httpResponse, 'Error while purging cache: %s');
                }
            }

            return true;
        } else {
            $params = array();
            if ($url) {
                $params["url"] = $url;
            }

            if ($pagecacheOnly) {
                $params["pagecache_only"] = true;
            }

            if ($reason) {
                $params["reason"] = $reason;
            }
            if ($lightPurge) {
                $params["light_purge"] = $lightPurge;
            }

            try {
                $httpResponse = $this->makeRequest($path, array(), array(), 'POST', $params);
            } catch (SocketReadTimedOutException $e) {
                return true;
            }

            $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
            switch ($status) {
            case ResponseStatus::OK:
                return true;
            default:
                $this->throwException($httpResponse, 'Error while purging cache: %s');
            }
        }

        return false;
    }

    public function purgeByTag($tag, $reason = NULL, $lightPurge = false) {
        $this->isBacklogEnabled = true;
        $path = 'cache/purge/' . $this->siteId;
        
        $params = array();
        
        $params["tag"] = $tag;

        if ($reason) {
            $params["reason"] = $reason;
        }

        if ($lightPurge) {
            $params["light_purge"] = $lightPurge;
        }

        try {
            $httpResponse = $this->makeRequest($path, array(), array(), 'POST', $params);
        } catch (SocketReadTimedOutException $e) {
            throw new \RuntimeException(sprintf('Timeout while purging cache by tag: %s', $tag));
        }

        $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
        switch ($status) {
        case ResponseStatus::OK:
            $body = json_decode($httpResponse->getBody(), true);

            return isset($body['purged_urls']) ? $body['purged_urls'] : array();
        default:
            $this->throwException($httpResponse, 'Error while purging cache by tag: %s');
        }

        return array();
    }

    public function enableCartCache() {
        $path = 'cache/enablecartcache/' . $this->siteId;

        $httpResponse = $this->makeRequest($path, array(), array(), 'POST');

        $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
        switch ($status) {
        case ResponseStatus::OK:
            return true;
        default:
            $this->throwException($httpResponse, 'Error while enabling cart cache: %s');
        }
    }

    public function disableCartCache() {
        $path = 'cache/disablecartcache/' . $this->siteId;

        $httpResponse = $this->makeRequest($path, array(), array(), 'POST');

        $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
        switch ($status) {
        case ResponseStatus::OK:
            return true;
        default:
            $this->throwException($httpResponse, 'Error while disabling cart cache: %s');
        }
    }

    private function isCacheWarmupRequest() {
        return isset($_SERVER['HTTP_X_NITRO_WARMUP']);
    }
}