1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\Queue; |
4
|
|
|
|
5
|
|
|
use DateInterval; |
6
|
|
|
use DateTime; |
7
|
|
|
use DateTimeInterface; |
8
|
|
|
use Exception; |
9
|
|
|
use InvalidArgumentException; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* |
13
|
|
|
*/ |
14
|
|
|
trait QueueItemTrait |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* The queue ID. |
18
|
|
|
* |
19
|
|
|
* @var mixed $queueId |
20
|
|
|
*/ |
21
|
|
|
private $queueId; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Whether the item has been processed. |
25
|
|
|
* |
26
|
|
|
* @var boolean $processed |
27
|
|
|
*/ |
28
|
|
|
private $processed = false; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* When the item was queued. |
32
|
|
|
* |
33
|
|
|
* @var DateTimeInterface|null $queuedDate |
34
|
|
|
*/ |
35
|
|
|
private $queuedDate; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* When the item should be processed. |
39
|
|
|
* |
40
|
|
|
* The date/time at which this queue item job should be ran. |
41
|
|
|
* If NULL, 0, or a past date/time, then it should be performed immediately. |
42
|
|
|
* |
43
|
|
|
* @var DateTimeInterface|null $processingDate |
44
|
|
|
*/ |
45
|
|
|
private $processingDate; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* When the item was processed. |
49
|
|
|
* |
50
|
|
|
* @var DateTimeInterface|null $processedDate |
51
|
|
|
*/ |
52
|
|
|
private $processedDate; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* When the item should be considered expired. |
56
|
|
|
* |
57
|
|
|
* The date/time at which this queue item job should expire and be prevented to fire. |
58
|
|
|
* If NULL, 0, or a future date/time, then it should be allowed to be performed. |
59
|
|
|
* |
60
|
|
|
* @var DateTimeInterface|null $lexpiryDate |
61
|
|
|
*/ |
62
|
|
|
private $expiryDate; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Default amount of seconds before expiry after processing date. |
66
|
|
|
* |
67
|
|
|
* @var integer $defaultExpiryInSeconde |
68
|
|
|
*/ |
69
|
|
|
private $defaultExpiryInSeconds = 84400; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Process the item. |
73
|
|
|
* |
74
|
|
|
* @param callable $alwaysCallback An optional callback routine executed after the item is processed. |
75
|
|
|
* @param callable $successCallback An optional callback routine executed when the item is resolved. |
76
|
|
|
* @param callable $failureCallback An optional callback routine executed when the item is rejected. |
77
|
|
|
* @return boolean|null Returns TRUE i this item was successfully processed, |
78
|
|
|
* FALSE on failure or if an error occurs, NULL if this item is already processed. |
79
|
|
|
*/ |
80
|
|
|
abstract public function process( |
81
|
|
|
callable $alwaysCallback = null, |
82
|
|
|
callable $successCallback = null, |
83
|
|
|
callable $failureCallback = null |
84
|
|
|
); |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Set the queue item's data. |
88
|
|
|
* |
89
|
|
|
* @param array $data The queue item data to set. |
90
|
|
|
* @return self |
91
|
|
|
*/ |
92
|
|
|
public function setQueueItemData(array $data) |
93
|
|
|
{ |
94
|
|
|
if (isset($data['queue_id'])) { |
95
|
|
|
$this->setQueueId($data['queue_id']); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
if (isset($data['processed'])) { |
99
|
|
|
$this->setProcessed($data['processed']); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
if (isset($data['queued_date'])) { |
103
|
|
|
$this->setQueuedDate($data['queue_date']); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
if (isset($data['processed_date'])) { |
107
|
|
|
$this->setProcessedDate($data['processed_date']); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $this; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Set the queue's ID. |
115
|
|
|
* |
116
|
|
|
* @param mixed $id The unique queue identifier. |
117
|
|
|
* @return self |
118
|
|
|
*/ |
119
|
|
|
public function setQueueId($id) |
120
|
|
|
{ |
121
|
|
|
$this->queueId = $id; |
122
|
|
|
return $this; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Get the queue's ID. |
127
|
|
|
* |
128
|
|
|
* @return mixed |
129
|
|
|
*/ |
130
|
|
|
public function queueId() |
131
|
|
|
{ |
132
|
|
|
return $this->queueId; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Set the item's processed status. |
137
|
|
|
* |
138
|
|
|
* @param boolean $processed Whether the item has been processed. |
139
|
|
|
* @return self |
140
|
|
|
*/ |
141
|
|
|
public function setProcessed($processed) |
142
|
|
|
{ |
143
|
|
|
$this->processed = !!$processed; |
144
|
|
|
return $this; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Determine if the item has been processed. |
149
|
|
|
* |
150
|
|
|
* @return boolean |
151
|
|
|
*/ |
152
|
|
|
public function processed() |
153
|
|
|
{ |
154
|
|
|
return $this->processed; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Set the date/time the item was queued at. |
159
|
|
|
* |
160
|
|
|
* @param null|string|DateTimeInterface $ts A date/time string or object. |
161
|
|
|
* @throws InvalidArgumentException If the date/time is invalid. |
162
|
|
|
* @return self |
163
|
|
|
*/ |
164
|
|
View Code Duplication |
public function setQueuedDate($ts) |
|
|
|
|
165
|
|
|
{ |
166
|
|
|
if ($ts === null) { |
167
|
|
|
$this->queuedDate = null; |
168
|
|
|
return $this; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
if (is_string($ts)) { |
172
|
|
|
try { |
173
|
|
|
$ts = new DateTime($ts); |
174
|
|
|
} catch (Exception $e) { |
175
|
|
|
throw new InvalidArgumentException( |
176
|
|
|
sprintf('Can not set queued date: %s', $e->getMessage()) |
177
|
|
|
); |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
if (!($ts instanceof DateTimeInterface)) { |
182
|
|
|
throw new InvalidArgumentException( |
183
|
|
|
'Invalid "Queued Date" value. Must be a date/time string or a DateTime object.' |
184
|
|
|
); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$this->queuedDate = $ts; |
188
|
|
|
|
189
|
|
|
return $this; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Retrieve the date/time the item was queued at. |
194
|
|
|
* |
195
|
|
|
* @return null|\DateTimeInterface |
196
|
|
|
*/ |
197
|
|
|
public function queuedDate() |
198
|
|
|
{ |
199
|
|
|
return $this->queuedDate; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Set the date/time the item should be processed at. |
204
|
|
|
* |
205
|
|
|
* @param null|string|\DateTimeInterface $ts A date/time string or object. |
206
|
|
|
* @throws InvalidArgumentException If the date/time is invalid. |
207
|
|
|
* @return self |
208
|
|
|
*/ |
209
|
|
View Code Duplication |
public function setProcessingDate($ts) |
|
|
|
|
210
|
|
|
{ |
211
|
|
|
if ($ts === null) { |
212
|
|
|
$this->processingDate = null; |
213
|
|
|
return $this; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
if (is_string($ts)) { |
217
|
|
|
try { |
218
|
|
|
$ts = new DateTime($ts); |
219
|
|
|
} catch (Exception $e) { |
220
|
|
|
throw new InvalidArgumentException( |
221
|
|
|
sprintf('%s (%s)', $e->getMessage(), $ts) |
222
|
|
|
); |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
if (!($ts instanceof DateTimeInterface)) { |
227
|
|
|
throw new InvalidArgumentException( |
228
|
|
|
'Invalid "Processing Date" value. Must be a date/time string or a DateTime object.' |
229
|
|
|
); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
$this->processingDate = $ts; |
233
|
|
|
|
234
|
|
|
return $this; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Retrieve the date/time the item should be processed at. |
239
|
|
|
* |
240
|
|
|
* @return null|\DateTimeInterface |
241
|
|
|
*/ |
242
|
|
|
public function processingDate() |
243
|
|
|
{ |
244
|
|
|
return $this->processingDate; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Set the date/time the item was processed at. |
249
|
|
|
* |
250
|
|
|
* @param null|string|\DateTimeInterface $ts A date/time string or object. |
251
|
|
|
* @throws InvalidArgumentException If the date/time is invalid. |
252
|
|
|
* @return self |
253
|
|
|
*/ |
254
|
|
View Code Duplication |
public function setProcessedDate($ts) |
|
|
|
|
255
|
|
|
{ |
256
|
|
|
if ($ts === null) { |
257
|
|
|
$this->processedDate = null; |
258
|
|
|
return $this; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
if (is_string($ts)) { |
262
|
|
|
try { |
263
|
|
|
$ts = new DateTime($ts); |
264
|
|
|
} catch (Exception $e) { |
265
|
|
|
throw new InvalidArgumentException( |
266
|
|
|
sprintf('%s (%s)', $e->getMessage(), $ts) |
267
|
|
|
); |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
if (!($ts instanceof DateTimeInterface)) { |
272
|
|
|
throw new InvalidArgumentException( |
273
|
|
|
'Invalid "Processed Date" value. Must be a date/time string or a DateTime object.' |
274
|
|
|
); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
$this->processedDate = $ts; |
278
|
|
|
|
279
|
|
|
return $this; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Retrieve the date/time the item was processed at. |
284
|
|
|
* |
285
|
|
|
* @return null|\DateTimeInterface |
286
|
|
|
*/ |
287
|
|
|
public function processedDate() |
288
|
|
|
{ |
289
|
|
|
return $this->processedDate; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Retrieve the date/time the item should be expired at. |
294
|
|
|
* |
295
|
|
|
* @return null|\DateTimeInterface |
296
|
|
|
*/ |
297
|
|
|
public function expiryDate() |
298
|
|
|
{ |
299
|
|
|
return $this->expiryDate; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Set the date/time the item will expire at. |
304
|
|
|
* |
305
|
|
|
* @param null|string|\DateTimeInterface $ts A date/time string or object. |
306
|
|
|
* @throws InvalidArgumentException If the date/time is invalid. |
307
|
|
|
* @return self |
308
|
|
|
*/ |
309
|
|
View Code Duplication |
public function setExpiryDate($ts) |
|
|
|
|
310
|
|
|
{ |
311
|
|
|
if ($ts === null) { |
312
|
|
|
$this->expiryDate = null; |
313
|
|
|
return $this; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
if (is_string($ts)) { |
317
|
|
|
try { |
318
|
|
|
$ts = new DateTime($ts); |
319
|
|
|
} catch (Exception $e) { |
320
|
|
|
throw new InvalidArgumentException( |
321
|
|
|
sprintf('%s (%s)', $e->getMessage(), $ts) |
322
|
|
|
); |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
if (!($ts instanceof DateTimeInterface)) { |
327
|
|
|
throw new InvalidArgumentException( |
328
|
|
|
'Invalid "Expiry Date" value. Must be a date/time string or a DateTime object.' |
329
|
|
|
); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
$this->expiryDate = $ts; |
333
|
|
|
|
334
|
|
|
return $this; |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Hook called before saving the item. |
339
|
|
|
* |
340
|
|
|
* Presets the item as _to-be_ processed and queued now. |
341
|
|
|
* |
342
|
|
|
* @return self |
343
|
|
|
*/ |
344
|
|
|
protected function preSaveQueueItem() |
345
|
|
|
{ |
346
|
|
|
$this->setProcessed(false); |
347
|
|
|
$this->setQueuedDate('now'); |
348
|
|
|
|
349
|
|
|
if (!$this->expiryDate()) { |
350
|
|
|
$this->generateExpiry(); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
return $this; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Generate an expiry date based on the default interval and the scheduled processing date. |
358
|
|
|
* |
359
|
|
|
* @return self |
360
|
|
|
*/ |
361
|
|
|
protected function generateExpiry() |
362
|
|
|
{ |
363
|
|
|
$date = (clone $this['processingDate'] ?? new DateTime()); |
364
|
|
|
$date->add(new DateInterval('PT'.$this->defaultExpiryInSeconds.'S')); |
365
|
|
|
|
366
|
|
|
return $this->setExpiryDate($date); |
367
|
|
|
} |
368
|
|
|
} |
369
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.