PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.5.2
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.5.2
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 / Backlog.php
nitropack / nitropack-sdk / NitroPack / SDK Last commit date
Api 5 years ago Integrations 6 years ago StorageDriver 5 years ago Url 6 years ago data 6 years ago Api.php 5 years ago Backlog.php 5 years ago Crypto.php 6 years ago Device.php 6 years ago DeviceType.php 6 years ago EmptyConfigException.php 6 years ago FileHandle.php 5 years ago Filesystem.php 5 years ago HealthStatus.php 5 years ago IntegrationUrl.php 6 years ago NitroPack.php 5 years ago NoConfigException.php 5 years ago Pagecache.php 5 years ago PurgeType.php 6 years ago ServiceDownException.php 5 years ago StorageException.php 6 years ago VariationCookieException.php 5 years ago WebhookException.php 5 years ago Website.php 6 years ago
Backlog.php
230 lines
1 <?php
2
3 namespace NitroPack\SDK;
4 use NitroPack\SDK\Api\ResponseStatus;
5
6 class Backlog {
7 const TTL = 86400; // 1 day in seconds
8
9 private $dataDir;
10 private $nitropack;
11 private $communicators;
12 private $queue;
13 private $queuePath;
14 private $isAcquired;
15 private $fileHandle;
16 private $header;
17 private $backlogFile = array('data', 'backlog.queue');
18
19 public function __construct($dataDir, $nitropack) {
20 $this->dataDir = $dataDir;
21 $this->nitropack = $nitropack;
22 $this->communicators = array();
23 $this->queue = array();
24 $this->queuePath = $this->getQueuePath();
25 $this->isAcquired = false;
26 $this->fileHandle = NULL;
27 $this->header = new \stdClass();
28 $this->header->offset = 0;
29 $this->header->firstProcessingTimestamp = 0;
30 $this->header->lastProcessingTimestamp = 0;
31 }
32
33 public function __destruct() {
34 $this->closeHandle();
35 }
36
37 public function append($entry) {
38 $fh = $this->getHandle();
39 Filesystem::flock($fh, LOCK_EX);
40 Filesystem::fseek($fh, 0, SEEK_END);
41 $this->writeEntry($fh, $this->encodeEntry($entry));
42 Filesystem::flock($fh, LOCK_UN);
43 }
44
45 public function replay($timeLimit = 10) {
46 $fh = $this->getHandle();
47 Filesystem::flock($fh, LOCK_EX);
48 $lastProcessingTimestamp = $this->header->lastProcessingTimestamp;
49 if (time() - $lastProcessingTimestamp <= $timeLimit) {
50 return;
51 }
52 $this->acquireBacklog($fh);
53 Filesystem::flock($fh, LOCK_UN);
54
55 $initialProcesssTime = $this->header->firstProcessingTimestamp;
56 if (time() - $initialProcesssTime > self::TTL) {
57 // In case there have been previous attempts at clearing the backlog and these attempts started more than the specified TTL seconds ago
58 // Perform a full purge and clear the backlog
59 }
60
61 if ($this->header->offset > 0) {
62 Filesystem::fseek($fh, $this->header->offset, SEEK_SET);
63 }
64 $start = microtime(true);
65 while (!$this->isEndOfQueue($fh) && NULL != ($entry = $this->getentry($fh)) && microtime(true) - $start < $timeLimit) {
66 $elapsedTime = microtime(true) - $start;
67 try {
68 $this->replayEntry($entry, $timeLimit - $elapsedTime);
69 } catch (\Exception $e) {
70 break;
71 }
72 }
73
74 if ($this->isEndOfQueue($fh)) {
75 $this->closeHandle();
76 Filesystem::deleteFile($this->queuePath);
77 }
78 }
79
80 private function isEndOfQueue($fh) {
81 return Filesystem::feof($fh);
82 }
83
84 public function dumpEntries() {
85 $fh = $this->getHandle();
86 while (!$this->isEndOfQueue($fh) && NULL != ($entry = $this->getentry($fh))) {
87 try {
88 var_dump($entry);
89 } catch (\Exception $e) {
90 break;
91 }
92 }
93 }
94
95 public function dumpHeader() {
96 $this->getHandle();
97 var_dump($this->header);
98 }
99
100 public function resetOffset() {
101 $fh = $this->getHandle();
102 $this->header->offset = 0;
103 $this->writeHeader($fh);
104 }
105
106 public function exists() {
107 return Filesystem::fileExists($this->queuePath);
108 }
109
110 private function replayEntry($entry, $timeLimit) {
111 if (array_key_exists("siteSecret", $entry)) {
112 $requestMaker = $this->nitropack->getApi()->secure_request_maker;
113 } else {
114 $requestMaker = $this->nitropack->getApi()->request_maker;
115 }
116 $httpResponse = $requestMaker->makeRequest($entry["path"], $entry["headers"], $entry["cookies"], $entry["type"], $entry["bodyData"], false, $entry["verifySSL"]);
117 $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
118 $headers = $httpResponse->getHeaders();
119
120 $start = microtime(true);
121 while ($status == ResponseStatus::OK && !empty($headers["x-nitro-repeat"]) && microtime(true) - $start < $timeLimit) {
122 $httpResponse->replay(); // In reality $httpResponse is an instance of HttpClient which has the replay method
123 $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
124 $headers = $httpResponse->getHeaders();
125 if ($status != ResponseStatus::OK) {
126 throw \RuntimeException("Unable to replay backlogged entry");
127 }
128 }
129
130 if ($status == ResponseStatus::OK && empty($headers["x-nitro-repeat"])) {
131 $fh = $this->getHandle();
132 $this->header->offset = Filesystem::ftell($fh);
133 $this->writeHeader($fh);
134 }
135 }
136
137 private function getQueuePath() {
138 $backlogFile = $this->backlogFile;
139 array_unshift($backlogFile, $this->dataDir);
140 return Filesystem::getOsPath($backlogFile);
141 }
142
143 private function getEntry($fh = NULL) {
144 $closeFile = empty($fh);
145 $fh = !empty($fh) ? $fh : $this->getHandle();
146 $entry = @Filesystem::fgets($fh);
147 if ($closeFile) {
148 Filesystem::fclose($fh);
149 }
150 return $this->decodeEntry($entry);
151 }
152
153 private function writeEntry($fh, $entry) {
154 Filesystem::fwrite($fh, $entry . "\n");
155 Filesystem::fflush($fh);
156 }
157
158 private function encodeEntry($entry) {
159 return base64_encode(json_encode($entry));
160 }
161
162 private function decodeEntry($entry) {
163 return json_decode(base64_decode($entry), true);
164 }
165
166 private function acquireBacklog($fh) {
167 $this->header->lastProcessingTimestamp = time();
168 if (!$this->header->firstProcessingTimestamp) {
169 $this->header->firstProcessingTimestamp = $this->header->lastProcessingTimestamp;
170 }
171 $this->writeHeader($fh);
172 $this->isAcquired = true;
173 }
174
175 private function releaseBacklog($fh) {
176 $this->header->lastProcessingTimestamp = 0;
177 $this->writeHeader($fh);
178 $this->isAcquired = false;
179 }
180
181 private function readHeader($fh) {
182 $offsetBackup = Filesystem::ftell($fh);
183 Filesystem::fseek($fh, 0, SEEK_SET);
184 $header = Filesystem::fread($fh, 12);
185 $parts = unpack("Loffset/LfirstProcessingTimestamp/LlastProcessingTimestamp", $header);
186 $this->header->offset = $parts["offset"];
187 $this->header->firstProcessingTimestamp = $parts["firstProcessingTimestamp"];
188 $this->header->lastProcessingTimestamp = $parts["lastProcessingTimestamp"];
189 Filesystem::fseek($fh, $offsetBackup, SEEK_SET);
190 }
191
192 private function writeHeader($fh) {
193 $offsetBackup = Filesystem::ftell($fh);
194 Filesystem::fseek($fh, 0, SEEK_SET);
195 Filesystem::fwrite($fh, pack("L*", $this->header->offset, $this->header->firstProcessingTimestamp, $this->header->lastProcessingTimestamp), 12);
196 Filesystem::fflush($fh);
197 Filesystem::fseek($fh, $offsetBackup, SEEK_SET);
198 }
199
200 private function getHandle() {
201 if ($this->fileHandle) return $this->fileHandle;
202
203 $fh = Filesystem::fopen($this->queuePath, "c+");
204 Filesystem::flock($fh, LOCK_EX);
205 Filesystem::fseek($fh, 0, SEEK_END);
206 $pos = Filesystem::ftell($fh);
207 if ($pos > 11) { // The first 12 bytes are reserved for the header. If the last pos is further than the 12th byte then the log is considered initialized
208 $this->readHeader($fh);
209 } else {
210 $this->writeHeader($fh);
211 }
212 Filesystem::fseek($fh, 12, SEEK_SET);
213 Filesystem::flock($fh, LOCK_UN);
214
215 $this->fileHandle = $fh;
216
217 return $this->fileHandle;
218 }
219
220 private function closeHandle() {
221 if ($this->fileHandle) {
222 if ($this->isAcquired) {
223 $this->releaseBacklog($this->fileHandle);
224 }
225 Filesystem::fclose($this->fileHandle);
226 $this->fileHandle = NULL;
227 }
228 }
229 }
230