PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.4.0
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.4.0
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / nitropack-sdk / NitroPack / SDK / NitroPack.php
nitropack / nitropack-sdk / NitroPack / SDK Last commit date
Api 6 years ago Integrations 6 years ago StorageDriver 5 years ago Url 6 years ago data 6 years ago Api.php 6 years ago Crypto.php 6 years ago Device.php 6 years ago DeviceType.php 6 years ago EmptyConfigException.php 6 years ago Filesystem.php 5 years ago IntegrationUrl.php 6 years ago NitroPack.php 5 years ago Pagecache.php 5 years ago PurgeType.php 6 years ago StorageException.php 6 years ago Website.php 6 years ago
NitroPack.php
710 lines
1 <?php
2 namespace NitroPack\SDK;
3
4 class NitroPack {
5 const VERSION = '0.19.2';
6 const PAGECACHE_LOCK_EXPIRATION_TIME = 300; // in seconds
7 private $dataDir;
8 private $cachePath = array('data', 'pagecache');
9 private $configFile = array('data', 'config.json');
10 private $pageCacheLockFile = array('data', 'get_cache.lock');
11 private $cachePathSuffix = NULL;
12 private $configTTL; // In seconds
13
14 private $siteId;
15 private $siteSecret;
16 private $userAgent; // Defaults to desktop Chrome
17
18 private $url;
19 private $config;
20 private $device;
21 public $pageCache; // TODO: consider better ways of protecting/providing this outside the class
22 private $api;
23
24 private static $cachePrefixes = array();
25 private static $cookieFilters = array();
26
27 public static function getRemoteAddr() {
28 // IP check order is: CloudFlare, Proxy, Client IP
29 $ipKeys = ["HTTP_X_FORWARDED_FOR", "HTTP_CF_CONNECTING_IP", "REMOTE_ADDR"];
30 foreach ($ipKeys as $key) {
31 if (!empty($_SERVER[$key])) {
32 return $_SERVER[$key];
33 }
34 }
35
36 return NULL;
37 }
38
39 public static function getCookies() {
40 $cookies = array();
41
42 foreach ($_COOKIE as $name=>$value) {
43 if (is_array($value)) {
44 foreach ($value as $k=>$v) {
45 $key = $name . "[$k]";
46 $cookies[$key] = $v;
47 }
48 } else {
49 $cookies[$name] = $value;
50 }
51 }
52
53 foreach (self::$cookieFilters as $cookieFilter) {
54 call_user_func_array($cookieFilter, array(&$cookies));
55 }
56
57 return $cookies;
58 }
59
60 public static function addCookieFilter($callback) {
61 if (is_callable($callback)) {
62 if (!in_array($callback, self::$cookieFilters, true)) {
63 self::$cookieFilters[] = $callback;
64 }
65 } else {
66 throw new \RuntimeException("Non-callable callback passed to " . __FUNCTION__);
67 }
68 }
69
70 public static function addCustomCachePrefix($prefix = "") {
71 self::$cachePrefixes[] = $prefix;
72 }
73
74 public static function getCustomCachePrefix() {
75 return implode("-", self::$cachePrefixes);
76 }
77
78 public static function wildcardToRegex($str, $delim = "/") {
79 return implode(".*?", array_map(function($input) use ($delim) { return preg_quote($input, $delim); }, explode("*", $str)));
80 }
81
82 public function __construct($siteId, $siteSecret, $userAgent = NULL, $url = NULL, $dataDir = __DIR__) {
83 $this->configTTL = 3600;
84 $this->siteId = $siteId;
85 $this->siteSecret = $siteSecret;
86 $this->dataDir = $dataDir;
87 if (empty($userAgent)) {
88 $this->userAgent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36';
89 } else {
90 $this->userAgent = $userAgent;
91 }
92
93 $this->loadConfig($siteId, $siteSecret);
94 $this->device = new Device($this->userAgent);
95
96 if(empty($url)) {
97 $host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : "example.com";
98 $uri = !empty($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/";
99 $url = $this->getScheme() . $host . $uri;
100 }
101
102 $queryStr = parse_url($url, PHP_URL_QUERY);
103
104 if ($queryStr) {
105 parse_str($queryStr, $queryParams);
106
107 if ($queryParams) {
108 if ($this->config->IgnoredParams) {
109 foreach ($this->config->IgnoredParams as $ignorePattern) {
110 $regex = "/^" . self::wildcardToRegex($ignorePattern) . "$/";
111 foreach($queryParams as $paramName => $paramValue) {
112 if (preg_match($regex, $paramName)) {
113 unset($queryParams[$paramName]);
114 }
115 }
116 }
117 }
118
119 ksort($queryParams);
120 $url = str_replace($queryStr, http_build_query($queryParams), $url);
121 }
122 }
123
124 $urlInfo = new \NitroPack\Url($url);
125 $this->url = $urlInfo->getNormalized();
126
127 $this->pageCache = new Pagecache($this->url, $this->userAgent, $this->supportedCookiesFilter(self::getCookies()), $this->config->PageCache->SupportedCookies, $this->isAJAXRequest());
128 if ($this->isAJAXRequest() && $this->isAllowedAJAXUrl($this->url) && !empty($_SERVER["HTTP_REFERER"])) {
129 $refererInfo = new \NitroPack\Url($_SERVER["HTTP_REFERER"]);
130 $this->pageCache->setReferer($refererInfo->getNormalized());
131 }
132
133 $this->api = new Api($this->siteId, $siteSecret);
134
135 $this->pageCache->setDataDir($this->getCacheDir());
136
137 $this->useCompression = false;
138 }
139
140 public function supportedCookiesFilter($cookies) {
141 $supportedCookies = array();
142 foreach ($cookies as $cookieName=>$cookieValue) {
143 foreach ($this->config->PageCache->SupportedCookies as $cookie) {
144 if (preg_match('/^' . self::wildcardToRegex($cookie) . '$/', $cookieName)) {
145 $supportedCookies[$cookieName] = $cookieValue;
146 }
147 }
148 }
149 return $supportedCookies;
150 }
151
152 public function tagUrl($url, $tag) {
153 if ($this->isAllowedUrl($url)) {
154 return $this->api->tagUrl($url, $tag);
155 } else {
156 return false;
157 }
158 }
159
160 public function setCachePathSuffix($suffix) {
161 $this->cachePathSuffix = $suffix;
162 $this->pageCache->setDataDir($this->getCacheDir());
163 }
164
165 public function enableCompression() {
166 $this->pageCache->enableCompression();
167 }
168
169 public function disableCompression() {
170 $this->pageCache->disableCompression();
171 }
172
173 public function getUrl() {
174 return $this->url;
175 }
176
177 public function getApi() {
178 return $this->api;
179 }
180
181 public function getSiteId() {
182 return $this->siteId;
183 }
184
185 public function getConfig() {
186 return $this->config;
187 }
188
189 public function getCacheDir() {
190 $cachePath = $this->cachePath;
191 array_unshift($cachePath, $this->dataDir);
192 if ($this->cachePathSuffix) {
193 $cachePath[] = $this->cachePathSuffix;
194 }
195 return Filesystem::getOsPath($cachePath);
196 }
197
198 public function hasCache($layout = 'default') {
199 if ($this->hasLocalCache()) {
200 return true;
201 } else {
202 return $this->hasRemoteCache($layout);
203 }
204 }
205
206 public function hasLocalCache($checkIfRequestIsAllowed = true) {
207 if (!$this->isAllowedUrl($this->url) || ($checkIfRequestIsAllowed && !$this->isAllowedRequest())) return false;
208 $cacheRevision = !empty($this->config->RevisionHash) ? $this->config->RevisionHash : NULL;
209
210 if (!$this->pageCache->getUseInvalidated()) {
211 $ttl = $this->config->PageCache->ExpireTime;
212 } else {
213 $ttl = $this->config->PageCache->StaleExpireTime;
214 }
215
216 return $this->pageCache->hasCache() && !$this->pageCache->hasExpired($ttl, $cacheRevision);
217 }
218
219 public function hasRemoteCache($layout, $checkIfRequestIsAllowed = true) {
220 if (!$this->isAllowedUrl($this->url) || ($checkIfRequestIsAllowed && !$this->isAllowedRequest()) || $this->isPageCacheLocked()) return false;
221 $resp = $this->api->getCache($this->url, $this->userAgent, $this->supportedCookiesFilter(self::getCookies()), $this->isAJAXRequest(), $layout);
222
223 if ($resp->getStatus() == Api\ResponseStatus::OK) {// We have cache response
224
225 // Check for invalidated cache and delete it if such is found
226 $this->pageCache->useInvalidated(true);
227 if ($this->pageCache->hasCache()) {
228 $path = $this->pageCache->getCachefilePath();
229 Filesystem::deleteFile($path);
230 Filesystem::deleteFile($path . ".gz");
231 Filesystem::deleteFile($path . ".stale");
232 Filesystem::deleteFile($path . ".stale.gz");
233 if (Filesystem::isDirEmpty(dirname($path))) {
234 Filesystem::deleteDir(dirname($path));
235 }
236 }
237 $this->pageCache->useInvalidated(false);
238 // End of check
239
240 $this->pageCache->setContent($resp->getBody());
241 return true;
242 } else {
243 // The goal is to serve cache at all times even when it is slightly outdated. This approach should be ok because new cache has been requested and it should be ready soon
244 if ($this->pageCache->hasCache()) {
245 return true;
246 } else {
247 // Check for invalidated cache
248 $this->pageCache->useInvalidated(true);
249 if ($this->hasLocalCache(false)) {
250 return true;
251 } else {
252 $this->pageCache->useInvalidated(false);
253 }
254 }
255
256 return false;
257 }
258 }
259
260 public function invalidateCache($url = NULL, $tag = NULL, $reason = NULL) {
261 return $this->purgeCache($url, $tag, PurgeType::INVALIDATE | PurgeType::PAGECACHE_ONLY, $reason);
262 }
263
264 public function clearPageCache($reason = NULL) {
265 return $this->purgeCache(NULL, NULL, PurgeType::PAGECACHE_ONLY, $reason);
266 }
267
268 public function purgeCache($url = NULL, $tag = NULL, $purgeType = PurgeType::COMPLETE, $reason = NULL) {
269 @set_time_limit(0);
270 $this->lockPageCache(); // Set the page cache lock, expires after self::PAGECACHE_LOCK_EXPIRATION_TIME seconds
271
272 try {
273 $invalidate = !!($purgeType & PurgeType::INVALIDATE);
274 $pageCacheOnly = !!($purgeType & PurgeType::PAGECACHE_ONLY);
275
276 if ($url || $tag) {
277 $localResult = true;
278 $apiResult = true;
279 if ($url) {
280 if (is_array($url)) {
281 foreach ($url as $urlLink) {
282 if ($invalidate) {
283 $localResult &= $this->invalidateLocalUrlCache($urlLink);
284 } else {
285 $localResult &= $this->purgeLocalUrlCache($urlLink);
286 }
287 }
288 $apiResult &= $this->api->purgeCache($url, false, $reason);
289 } else {
290 if ($invalidate) {
291 $localResult &= $this->invalidateLocalUrlCache($url);
292 } else {
293 $localResult &= $this->purgeLocalUrlCache($url);
294 }
295 $apiResult &= $this->api->purgeCache($url, false, $reason);
296 }
297 }
298
299 if ($tag) {
300 $attemptsLeft = 10;
301 $purgedUrls = array();
302 do {
303 $hadError = false;
304
305 try {
306 $purgedUrls = $this->api->purgeCacheByTag($tag, $reason);
307
308 foreach ($purgedUrls as $url) {
309 if ($invalidate) {
310 $localResult &= $this->invalidateLocalUrlCache($url);
311 } else {
312 $localResult &= $this->purgeLocalUrlCache($url);
313 }
314 }
315 } catch (\Exception $e) {
316 $hadError = true;
317 $attemptsLeft--;
318 sleep(3);
319 }
320 } while (($hadError && $attemptsLeft > 0) || count($purgedUrls) > 0);
321 }
322 } else {
323 if ($invalidate) {
324 $localResult = $this->invalidateLocalCache();
325 $apiResult = $this->api->purgeCache(NULL, $pageCacheOnly, $reason); // delete only page cache
326 } else {
327 $staleCacheDir = $this->purgeLocalCache(true);
328
329 // Call the cache purge method
330 $apiResult = $this->api->purgeCache(NULL, $pageCacheOnly, $reason);
331
332 // Finally, delete the files of the stale directory
333 Filesystem::deleteDir($staleCacheDir);
334
335 $localResult = true; // We do not care if $staleCacheDir was not deleted successfully
336 }
337 }
338
339 $this->unlockPageCache(); // Purge cache is done, we can now unlock
340 } catch (\Exception $e) {
341 $this->unlockPageCache(); // Purge cache had an error, so just unlock
342
343 throw $e;
344 }
345
346 return $apiResult && $localResult;
347 }
348
349 public function purgeLocalCache($quick = false) {
350 $staleCacheDir = $this->getCacheDir() . '.stale.' . md5(microtime(true));
351 $this->purgeProxyCache();
352 $this->config->LastFetch = 0;
353 $this->setConfig($this->config);
354
355 // Rename cache files directory
356 if (Filesystem::fileExists($this->getCacheDir()) && !Filesystem::rename($this->getCacheDir(), $staleCacheDir)) {
357 throw new \Exception("No write permissions to rename the directory: " . $this->getCacheDir());
358 }
359
360 // Create a new empty directory
361 if (!Filesystem::createDir($this->getCacheDir())) {
362 throw new \Exception("No write permissions to create the directory: " . $this->getCacheDir());
363 }
364
365 if (!$quick) {
366 // Finally, delete the files of the stale directory
367 Filesystem::deleteDir($staleCacheDir);
368 }
369
370 return $staleCacheDir;
371 }
372
373 public function fetchConfig() {
374 $fetcher = new Api\RemoteConfigFetcher($this->siteId, $this->siteSecret);
375 $configContents = $fetcher->get(); // this can throw in case of http errors or validation failures
376 $config = json_decode($configContents);
377 if ($config) {
378 $config->SDKVersion = NitroPack::VERSION;
379 $config->LastFetch = time();
380
381 $this->setConfig($config);
382 return true;
383 } else {
384 throw new EmptyConfigException("Config response was empty");
385 }
386 }
387
388 public function setConfig($config) {
389 $file = $this->getConfigFile();
390 if (Filesystem::createDir(dirname($file))) {
391 if (Filesystem::filePutContents($file, json_encode($config))) {
392 return true;
393 } else {
394 throw new StorageException(sprintf("Config file %s cannot be saved to disk", $file));
395 }
396 } else {
397 throw new StorageException(sprintf("Storage directory %s cannot be created", dirname($file)));
398 }
399 }
400
401 public function purgeProxyCache($url = NULL) {
402 if (!empty($this->config->CacheIntegrations)) {
403 if (!empty($this->config->CacheIntegrations->Varnish)) {
404 if ($url) {
405 $varnish = new Integrations\Varnish($this->config->CacheIntegrations->Varnish->Servers, $this->config->CacheIntegrations->Varnish->PurgeSingleMethod);
406 $varnish->purge($url);
407 } else {
408 $varnish = new Integrations\Varnish($this->config->CacheIntegrations->Varnish->Servers, $this->config->CacheIntegrations->Varnish->PurgeAllMethod);
409 $varnish->purge($this->config->CacheIntegrations->Varnish->PurgeAllUrl);
410 }
411 }
412
413 //if (!empty($this->config->CacheIntegrations->LiteSpeed) && php_sapi_name() !== "cli") {
414 // if ($url) {
415 // $urlObj = new \NitroPack\Url($url);
416 // $liteSpeedPath = $urlObj->getPath();
417 // if ($urlObj->getQuery()) {
418 // $liteSpeedPath .= "?" . $urlObj->getQuery();
419 // }
420 // header("X-LiteSpeed-Purge: $liteSpeedPath", false);
421 // } else {
422 // header("X-LiteSpeed-Purge: *", false);
423 // }
424 //}
425 }
426 }
427
428 public function isAllowedUrl($url) {
429 if (strpos($url, 'sucurianticache=') !== false) return false;
430
431 if ($this->config->EnabledURLs->Status) {
432 if (!empty($this->config->EnabledURLs->URLs)) {
433 foreach ($this->config->EnabledURLs->URLs as $enabledUrl) {
434 $enabledUrlModified = preg_replace("/^(https?:)?\/\//", "*", $enabledUrl);
435
436 if (preg_match('/^' . self::wildcardToRegex($enabledUrlModified) . '$/', $url)) {
437 return true;
438 }
439 }
440
441 return false;
442 }
443 } else if ($this->config->DisabledURLs->Status) {
444 if (!empty($this->config->DisabledURLs->URLs)) {
445 foreach ($this->config->DisabledURLs->URLs as $disabledUrl) {
446 $disabledUrlModified = preg_replace("/^(https?:)?\/\//", "*", $disabledUrl);
447
448 if (preg_match('/^' . self::wildcardToRegex($disabledUrlModified) . '$/', $url)) {
449 return false; // don't cache disabled URLs
450 }
451 }
452 }
453 }
454
455 return true;
456 }
457
458 public function isAllowedRequest($allowServiceRequests = false) {
459 if (($this->isAJAXRequest() && !$this->isAllowedAJAX()) || !($this->isRequestMethod("GET") || $this->isRequestMethod("HEAD"))) {// TODO: Allow URLs which match a pattern in the AJAX URL whitelist
460 return false; // don't cache ajax or not GET requests
461 }
462
463 if (!$allowServiceRequests && isset($_SERVER["HTTP_X_NITROPACK_REQUEST"])) { // Skip requests coming from NitroPack
464 return false;
465 }
466
467 if (isset($_GET["nonitro"])) { // Skip requests having ?nonitro
468 return false;
469 }
470
471 if (!$this->isAllowedBrowser()) {
472 return false;
473 }
474
475 if (isset($this->config->ExcludedCookies) && $this->config->ExcludedCookies->Status) {
476 foreach ($this->config->ExcludedCookies->Cookies as $cookieExclude) {
477 foreach (self::getCookies() as $cookieName => $cookieValue) {
478 if (preg_match('/^' . self::wildcardToRegex($cookieExclude->name) . '$/', $cookieName)) {
479 if (count($cookieExclude->values) == 0) {
480 return false; // no excluded cookie values entered, reject all values
481 } else {
482 foreach ($cookieExclude->values as $val) {
483 if (preg_match('/^' . self::wildcardToRegex($val) . '$/', $cookieValue)) {
484 return false;
485 }
486 }
487 }
488 }
489 }
490 }
491 }
492
493 return true;
494 }
495
496 public function isAllowedBrowser() {
497 if (empty($_SERVER["HTTP_USER_AGENT"])) return true;
498
499 if (preg_match("~MSIE|Internet Explorer~i", $_SERVER["HTTP_USER_AGENT"]) || strpos($_SERVER["HTTP_USER_AGENT"], "Trident/7.0; rv:11.0") !== false) { // Skip IE
500 return false;
501 }
502
503 return true;
504 }
505
506 public function getScheme() {
507 return $this->isSecure() ? 'https://' : 'http://';
508 }
509
510 public function isSecure() {
511 return (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && in_array("https", array_map("strtolower", array_map("trim", explode(",", $_SERVER['HTTP_X_FORWARDED_PROTO']))))) ||
512 (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
513 (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) ||
514 (!empty($_SERVER['HTTP_SSL_FLAG']) && $_SERVER['HTTP_SSL_FLAG'] == 'SSL');
515 }
516
517 public function isAJAXRequest() {
518 return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
519 }
520
521 public function isRequestMethod($method) {
522 return empty($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] == $method;
523 }
524
525 public function isAllowedAJAX() {
526 if (!$this->pageCache->getParent()) return false;
527 if (!$this->pageCache->getParent()->hasCache() || $this->pageCache->getParent()->hasExpired($this->config->PageCache->ExpireTime)) return false;
528 return true;
529 }
530
531 public function isAllowedAJAXUrl($url) {
532 if ($this->config->AjaxURLs->Status) {
533 if (!empty($this->config->AjaxURLs->URLs)) {
534 foreach ($this->config->AjaxURLs->URLs as $ajaxUrl) {
535 $ajaxUrlModified = preg_replace("/^(https?:)?\/\//", "*", $ajaxUrl);
536 if (preg_match('/^' . self::wildcardToRegex($ajaxUrlModified) . '$/', $url)) {
537 return true;
538 }
539 }
540 return false;
541 }
542 }
543 return false;
544 }
545
546 public function isCacheAllowed() {
547 return $this->isAllowedRequest() && $this->isAllowedUrl($this->url);
548 }
549
550 public function purgeLocalUrlCache($url) {
551 $this->purgeProxyCache($url);
552 $localResult = true;
553 $cacheDir = $this->getCacheDir();
554 $knownDeviceTypes = Device::getKnownTypes();
555 foreach ($knownDeviceTypes as $deviceType) {
556 $urlDir = PageCache::getUrlDir($cacheDir . "/" . $deviceType, $url);
557 $invalidatedUrlDir = PageCache::getUrlDir($cacheDir . "/" . $deviceType, $url, true);
558 $localResult &= Filesystem::deleteDir($urlDir);
559 $localResult &= Filesystem::deleteDir($invalidatedUrlDir);
560 }
561 return $localResult;
562 }
563
564 public function invalidateLocalUrlCache($url) {
565 $this->purgeProxyCache($url);
566 $localResult = true;
567 $cacheDir = $this->getCacheDir();
568 $knownDeviceTypes = Device::getKnownTypes();
569 foreach ($knownDeviceTypes as $deviceType) {
570 $urlDir = PageCache::getUrlDir($cacheDir . "/" . $deviceType, $url);
571 $urlDirInvalid = PageCache::getUrlDir($cacheDir . "/" . $deviceType, $url, true);
572
573 $this->invalidateDir($urlDir, $urlDirInvalid);
574 }
575 return $localResult;
576 }
577
578 public function invalidateLocalCache() {
579 $this->purgeProxyCache();
580 $this->config->LastFetch = 0;
581 $this->setConfig($this->config);
582
583 $cacheDir = $this->getCacheDir();
584 $knownDeviceTypes = Device::getKnownTypes();
585 foreach ($knownDeviceTypes as $deviceType) {
586 $deviceTypeDir = $cacheDir . "/" . $deviceType;
587 Filesystem::dirForeach($deviceTypeDir, function($urlDir) {
588 if (substr($urlDir, -2) !== "_i") {
589 $this->invalidateDir($urlDir, $urlDir . "_i");
590 }
591 });
592 }
593 return true;
594 }
595
596 private function invalidateDir($urlDir, $urlDirInvalid) {
597 if (Filesystem::fileExists($urlDirInvalid)) {
598 Filesystem::dirForeach($urlDir, function($file) use ($urlDirInvalid) {
599 Filesystem::rename($file, $urlDirInvalid . "/" . basename($file));
600 });
601
602 Filesystem::deleteDir($urlDir);
603 } else {
604 Filesystem::rename($urlDir, $urlDirInvalid);
605 }
606 Filesystem::touch($urlDirInvalid);
607 }
608
609 public function integrationUrl($widget, $version = null) {
610 $integration = new IntegrationUrl($widget, $this->siteId, $this->siteSecret, $version);
611
612 return $integration->getUrl();
613 }
614
615 public function embedJsUrl() {
616 $embedjs = new Url\Embedjs();
617
618 return $embedjs->getUrl();
619 }
620
621 private function loadConfig() {
622 $file = $this->getConfigFile();
623
624 $config = array();
625 if (Filesystem::fileExists($file) || $this->fetchConfig()) {
626 $config = json_decode(Filesystem::fileGetContents($file));
627 if (empty($config->SDKVersion) || $config->SDKVersion !== NitroPack::VERSION || empty($config->LastFetch) || time() - $config->LastFetch >= $this->configTTL) {
628 if ($this->fetchConfig()) {
629 $config = json_decode(Filesystem::fileGetContents($file));
630 } else {
631 throw new NoConfigException("Can't load config file");
632 }
633 }
634 $this->config = $config;
635 } else {
636 throw new NoConfigException("Can't load config file");
637 }
638 }
639
640 private function getConfigFile() {
641 $configFile = $this->configFile;
642
643 $filename = array_pop($configFile);
644
645 $filename = $this->siteId . '-' . $filename;
646
647 array_push($configFile, $filename);
648 array_unshift($configFile, $this->dataDir);
649
650 return Filesystem::getOsPath($configFile);
651 }
652
653 private function lockPageCache() {
654 $filename = $this->getPageCacheLockFilename();
655
656 if (Filesystem::fileExists($filename)) {
657 $sem = 1 + (int)Filesystem::fileGetContents($filename);
658 } else {
659 $sem = 1;
660 }
661
662 return !!Filesystem::filePutContents($filename, $sem);
663 }
664
665 private function unlockPageCache() {
666 $filename = $this->getPageCacheLockFilename();
667
668 if (Filesystem::fileExists($filename)) {
669 $sem = (int)Filesystem::fileGetContents($filename);
670
671 $sem--;
672
673 if ($sem <= 0) {
674 return !!Filesystem::deleteFile($filename);
675 } else {
676 return !!Filesystem::filePutContents($filename, $sem);
677 }
678 }
679
680 return false;
681 }
682
683 private function isPageCacheLocked() {
684 $filename = $this->getPageCacheLockFilename();
685
686 if (!Filesystem::fileExists($filename)) {
687 return false;
688 } else {
689 if (time() - Filesystem::fileMTime($filename) <= self::PAGECACHE_LOCK_EXPIRATION_TIME) {
690 return true;
691 } else {
692 Filesystem::deleteFile($filename);
693
694 return false;
695 }
696 }
697
698 // We should never get here, so consider this a default return value in case of future changes
699 return false;
700 }
701
702 private function getPageCacheLockFilename() {
703 $pageCacheLockFile = $this->pageCacheLockFile;
704 array_unshift($pageCacheLockFile, $this->dataDir);
705 return Filesystem::getOsPath($pageCacheLockFile);
706 }
707 }
708
709 class NoConfigException extends \Exception {}
710