diff options
Diffstat (limited to 'application/bookmark/Bookmark.php')
-rw-r--r-- | application/bookmark/Bookmark.php | 202 |
1 files changed, 134 insertions, 68 deletions
diff --git a/application/bookmark/Bookmark.php b/application/bookmark/Bookmark.php index f9b21d3d..4810c5e6 100644 --- a/application/bookmark/Bookmark.php +++ b/application/bookmark/Bookmark.php | |||
@@ -1,8 +1,11 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | declare(strict_types=1); | ||
4 | |||
3 | namespace Shaarli\Bookmark; | 5 | namespace Shaarli\Bookmark; |
4 | 6 | ||
5 | use DateTime; | 7 | use DateTime; |
8 | use DateTimeInterface; | ||
6 | use Shaarli\Bookmark\Exception\InvalidBookmarkException; | 9 | use Shaarli\Bookmark\Exception\InvalidBookmarkException; |
7 | 10 | ||
8 | /** | 11 | /** |
@@ -36,21 +39,24 @@ class Bookmark | |||
36 | /** @var array List of bookmark's tags */ | 39 | /** @var array List of bookmark's tags */ |
37 | protected $tags; | 40 | protected $tags; |
38 | 41 | ||
39 | /** @var string Thumbnail's URL - false if no thumbnail could be found */ | 42 | /** @var string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found */ |
40 | protected $thumbnail; | 43 | protected $thumbnail; |
41 | 44 | ||
42 | /** @var bool Set to true if the bookmark is set as sticky */ | 45 | /** @var bool Set to true if the bookmark is set as sticky */ |
43 | protected $sticky; | 46 | protected $sticky; |
44 | 47 | ||
45 | /** @var DateTime Creation datetime */ | 48 | /** @var DateTimeInterface Creation datetime */ |
46 | protected $created; | 49 | protected $created; |
47 | 50 | ||
48 | /** @var DateTime Update datetime */ | 51 | /** @var DateTimeInterface datetime */ |
49 | protected $updated; | 52 | protected $updated; |
50 | 53 | ||
51 | /** @var bool True if the bookmark can only be seen while logged in */ | 54 | /** @var bool True if the bookmark can only be seen while logged in */ |
52 | protected $private; | 55 | protected $private; |
53 | 56 | ||
57 | /** @var mixed[] Available to store any additional content for a bookmark. Currently used for search highlight. */ | ||
58 | protected $additionalContent = []; | ||
59 | |||
54 | /** | 60 | /** |
55 | * Initialize a link from array data. Especially useful to create a Bookmark from former link storage format. | 61 | * Initialize a link from array data. Especially useful to create a Bookmark from former link storage format. |
56 | * | 62 | * |
@@ -58,25 +64,25 @@ class Bookmark | |||
58 | * | 64 | * |
59 | * @return $this | 65 | * @return $this |
60 | */ | 66 | */ |
61 | public function fromArray($data) | 67 | public function fromArray(array $data): Bookmark |
62 | { | 68 | { |
63 | $this->id = $data['id']; | 69 | $this->id = $data['id'] ?? null; |
64 | $this->shortUrl = $data['shorturl']; | 70 | $this->shortUrl = $data['shorturl'] ?? null; |
65 | $this->url = $data['url']; | 71 | $this->url = $data['url'] ?? null; |
66 | $this->title = $data['title']; | 72 | $this->title = $data['title'] ?? null; |
67 | $this->description = $data['description']; | 73 | $this->description = $data['description'] ?? null; |
68 | $this->thumbnail = isset($data['thumbnail']) ? $data['thumbnail'] : null; | 74 | $this->thumbnail = $data['thumbnail'] ?? null; |
69 | $this->sticky = isset($data['sticky']) ? $data['sticky'] : false; | 75 | $this->sticky = $data['sticky'] ?? false; |
70 | $this->created = $data['created']; | 76 | $this->created = $data['created'] ?? null; |
71 | if (is_array($data['tags'])) { | 77 | if (is_array($data['tags'])) { |
72 | $this->tags = $data['tags']; | 78 | $this->tags = $data['tags']; |
73 | } else { | 79 | } else { |
74 | $this->tags = preg_split('/\s+/', $data['tags'], -1, PREG_SPLIT_NO_EMPTY); | 80 | $this->tags = preg_split('/\s+/', $data['tags'] ?? '', -1, PREG_SPLIT_NO_EMPTY); |
75 | } | 81 | } |
76 | if (! empty($data['updated'])) { | 82 | if (! empty($data['updated'])) { |
77 | $this->updated = $data['updated']; | 83 | $this->updated = $data['updated']; |
78 | } | 84 | } |
79 | $this->private = $data['private'] ? true : false; | 85 | $this->private = ($data['private'] ?? false) ? true : false; |
80 | 86 | ||
81 | return $this; | 87 | return $this; |
82 | } | 88 | } |
@@ -92,24 +98,28 @@ class Bookmark | |||
92 | * - the URL with the permalink | 98 | * - the URL with the permalink |
93 | * - the title with the URL | 99 | * - the title with the URL |
94 | * | 100 | * |
101 | * Also make sure that we do not save search highlights in the datastore. | ||
102 | * | ||
95 | * @throws InvalidBookmarkException | 103 | * @throws InvalidBookmarkException |
96 | */ | 104 | */ |
97 | public function validate() | 105 | public function validate(): void |
98 | { | 106 | { |
99 | if ($this->id === null | 107 | if ($this->id === null |
100 | || ! is_int($this->id) | 108 | || ! is_int($this->id) |
101 | || empty($this->shortUrl) | 109 | || empty($this->shortUrl) |
102 | || empty($this->created) | 110 | || empty($this->created) |
103 | || ! $this->created instanceof DateTime | ||
104 | ) { | 111 | ) { |
105 | throw new InvalidBookmarkException($this); | 112 | throw new InvalidBookmarkException($this); |
106 | } | 113 | } |
107 | if (empty($this->url)) { | 114 | if (empty($this->url)) { |
108 | $this->url = '?'. $this->shortUrl; | 115 | $this->url = '/shaare/'. $this->shortUrl; |
109 | } | 116 | } |
110 | if (empty($this->title)) { | 117 | if (empty($this->title)) { |
111 | $this->title = $this->url; | 118 | $this->title = $this->url; |
112 | } | 119 | } |
120 | if (array_key_exists('search_highlight', $this->additionalContent)) { | ||
121 | unset($this->additionalContent['search_highlight']); | ||
122 | } | ||
113 | } | 123 | } |
114 | 124 | ||
115 | /** | 125 | /** |
@@ -118,11 +128,11 @@ class Bookmark | |||
118 | * - created: with the current datetime | 128 | * - created: with the current datetime |
119 | * - shortUrl: with a generated small hash from the date and the given ID | 129 | * - shortUrl: with a generated small hash from the date and the given ID |
120 | * | 130 | * |
121 | * @param int $id | 131 | * @param int|null $id |
122 | * | 132 | * |
123 | * @return Bookmark | 133 | * @return Bookmark |
124 | */ | 134 | */ |
125 | public function setId($id) | 135 | public function setId(?int $id): Bookmark |
126 | { | 136 | { |
127 | $this->id = $id; | 137 | $this->id = $id; |
128 | if (empty($this->created)) { | 138 | if (empty($this->created)) { |
@@ -138,9 +148,9 @@ class Bookmark | |||
138 | /** | 148 | /** |
139 | * Get the Id. | 149 | * Get the Id. |
140 | * | 150 | * |
141 | * @return int | 151 | * @return int|null |
142 | */ | 152 | */ |
143 | public function getId() | 153 | public function getId(): ?int |
144 | { | 154 | { |
145 | return $this->id; | 155 | return $this->id; |
146 | } | 156 | } |
@@ -148,9 +158,9 @@ class Bookmark | |||
148 | /** | 158 | /** |
149 | * Get the ShortUrl. | 159 | * Get the ShortUrl. |
150 | * | 160 | * |
151 | * @return string | 161 | * @return string|null |
152 | */ | 162 | */ |
153 | public function getShortUrl() | 163 | public function getShortUrl(): ?string |
154 | { | 164 | { |
155 | return $this->shortUrl; | 165 | return $this->shortUrl; |
156 | } | 166 | } |
@@ -158,9 +168,9 @@ class Bookmark | |||
158 | /** | 168 | /** |
159 | * Get the Url. | 169 | * Get the Url. |
160 | * | 170 | * |
161 | * @return string | 171 | * @return string|null |
162 | */ | 172 | */ |
163 | public function getUrl() | 173 | public function getUrl(): ?string |
164 | { | 174 | { |
165 | return $this->url; | 175 | return $this->url; |
166 | } | 176 | } |
@@ -170,7 +180,7 @@ class Bookmark | |||
170 | * | 180 | * |
171 | * @return string | 181 | * @return string |
172 | */ | 182 | */ |
173 | public function getTitle() | 183 | public function getTitle(): ?string |
174 | { | 184 | { |
175 | return $this->title; | 185 | return $this->title; |
176 | } | 186 | } |
@@ -180,7 +190,7 @@ class Bookmark | |||
180 | * | 190 | * |
181 | * @return string | 191 | * @return string |
182 | */ | 192 | */ |
183 | public function getDescription() | 193 | public function getDescription(): string |
184 | { | 194 | { |
185 | return ! empty($this->description) ? $this->description : ''; | 195 | return ! empty($this->description) ? $this->description : ''; |
186 | } | 196 | } |
@@ -188,9 +198,9 @@ class Bookmark | |||
188 | /** | 198 | /** |
189 | * Get the Created. | 199 | * Get the Created. |
190 | * | 200 | * |
191 | * @return DateTime | 201 | * @return DateTimeInterface |
192 | */ | 202 | */ |
193 | public function getCreated() | 203 | public function getCreated(): ?DateTimeInterface |
194 | { | 204 | { |
195 | return $this->created; | 205 | return $this->created; |
196 | } | 206 | } |
@@ -198,9 +208,9 @@ class Bookmark | |||
198 | /** | 208 | /** |
199 | * Get the Updated. | 209 | * Get the Updated. |
200 | * | 210 | * |
201 | * @return DateTime | 211 | * @return DateTimeInterface |
202 | */ | 212 | */ |
203 | public function getUpdated() | 213 | public function getUpdated(): ?DateTimeInterface |
204 | { | 214 | { |
205 | return $this->updated; | 215 | return $this->updated; |
206 | } | 216 | } |
@@ -208,11 +218,11 @@ class Bookmark | |||
208 | /** | 218 | /** |
209 | * Set the ShortUrl. | 219 | * Set the ShortUrl. |
210 | * | 220 | * |
211 | * @param string $shortUrl | 221 | * @param string|null $shortUrl |
212 | * | 222 | * |
213 | * @return Bookmark | 223 | * @return Bookmark |
214 | */ | 224 | */ |
215 | public function setShortUrl($shortUrl) | 225 | public function setShortUrl(?string $shortUrl): Bookmark |
216 | { | 226 | { |
217 | $this->shortUrl = $shortUrl; | 227 | $this->shortUrl = $shortUrl; |
218 | 228 | ||
@@ -222,14 +232,14 @@ class Bookmark | |||
222 | /** | 232 | /** |
223 | * Set the Url. | 233 | * Set the Url. |
224 | * | 234 | * |
225 | * @param string $url | 235 | * @param string|null $url |
226 | * @param array $allowedProtocols | 236 | * @param string[] $allowedProtocols |
227 | * | 237 | * |
228 | * @return Bookmark | 238 | * @return Bookmark |
229 | */ | 239 | */ |
230 | public function setUrl($url, $allowedProtocols = []) | 240 | public function setUrl(?string $url, array $allowedProtocols = []): Bookmark |
231 | { | 241 | { |
232 | $url = trim($url); | 242 | $url = $url !== null ? trim($url) : ''; |
233 | if (! empty($url)) { | 243 | if (! empty($url)) { |
234 | $url = whitelist_protocols($url, $allowedProtocols); | 244 | $url = whitelist_protocols($url, $allowedProtocols); |
235 | } | 245 | } |
@@ -241,13 +251,13 @@ class Bookmark | |||
241 | /** | 251 | /** |
242 | * Set the Title. | 252 | * Set the Title. |
243 | * | 253 | * |
244 | * @param string $title | 254 | * @param string|null $title |
245 | * | 255 | * |
246 | * @return Bookmark | 256 | * @return Bookmark |
247 | */ | 257 | */ |
248 | public function setTitle($title) | 258 | public function setTitle(?string $title): Bookmark |
249 | { | 259 | { |
250 | $this->title = trim($title); | 260 | $this->title = $title !== null ? trim($title) : ''; |
251 | 261 | ||
252 | return $this; | 262 | return $this; |
253 | } | 263 | } |
@@ -255,11 +265,11 @@ class Bookmark | |||
255 | /** | 265 | /** |
256 | * Set the Description. | 266 | * Set the Description. |
257 | * | 267 | * |
258 | * @param string $description | 268 | * @param string|null $description |
259 | * | 269 | * |
260 | * @return Bookmark | 270 | * @return Bookmark |
261 | */ | 271 | */ |
262 | public function setDescription($description) | 272 | public function setDescription(?string $description): Bookmark |
263 | { | 273 | { |
264 | $this->description = $description; | 274 | $this->description = $description; |
265 | 275 | ||
@@ -270,11 +280,11 @@ class Bookmark | |||
270 | * Set the Created. | 280 | * Set the Created. |
271 | * Note: you shouldn't set this manually except for special cases (like bookmark import) | 281 | * Note: you shouldn't set this manually except for special cases (like bookmark import) |
272 | * | 282 | * |
273 | * @param DateTime $created | 283 | * @param DateTimeInterface|null $created |
274 | * | 284 | * |
275 | * @return Bookmark | 285 | * @return Bookmark |
276 | */ | 286 | */ |
277 | public function setCreated($created) | 287 | public function setCreated(?DateTimeInterface $created): Bookmark |
278 | { | 288 | { |
279 | $this->created = $created; | 289 | $this->created = $created; |
280 | 290 | ||
@@ -284,11 +294,11 @@ class Bookmark | |||
284 | /** | 294 | /** |
285 | * Set the Updated. | 295 | * Set the Updated. |
286 | * | 296 | * |
287 | * @param DateTime $updated | 297 | * @param DateTimeInterface|null $updated |
288 | * | 298 | * |
289 | * @return Bookmark | 299 | * @return Bookmark |
290 | */ | 300 | */ |
291 | public function setUpdated($updated) | 301 | public function setUpdated(?DateTimeInterface $updated): Bookmark |
292 | { | 302 | { |
293 | $this->updated = $updated; | 303 | $this->updated = $updated; |
294 | 304 | ||
@@ -300,7 +310,7 @@ class Bookmark | |||
300 | * | 310 | * |
301 | * @return bool | 311 | * @return bool |
302 | */ | 312 | */ |
303 | public function isPrivate() | 313 | public function isPrivate(): bool |
304 | { | 314 | { |
305 | return $this->private ? true : false; | 315 | return $this->private ? true : false; |
306 | } | 316 | } |
@@ -308,11 +318,11 @@ class Bookmark | |||
308 | /** | 318 | /** |
309 | * Set the Private. | 319 | * Set the Private. |
310 | * | 320 | * |
311 | * @param bool $private | 321 | * @param bool|null $private |
312 | * | 322 | * |
313 | * @return Bookmark | 323 | * @return Bookmark |
314 | */ | 324 | */ |
315 | public function setPrivate($private) | 325 | public function setPrivate(?bool $private): Bookmark |
316 | { | 326 | { |
317 | $this->private = $private ? true : false; | 327 | $this->private = $private ? true : false; |
318 | 328 | ||
@@ -322,9 +332,9 @@ class Bookmark | |||
322 | /** | 332 | /** |
323 | * Get the Tags. | 333 | * Get the Tags. |
324 | * | 334 | * |
325 | * @return array | 335 | * @return string[] |
326 | */ | 336 | */ |
327 | public function getTags() | 337 | public function getTags(): array |
328 | { | 338 | { |
329 | return is_array($this->tags) ? $this->tags : []; | 339 | return is_array($this->tags) ? $this->tags : []; |
330 | } | 340 | } |
@@ -332,13 +342,13 @@ class Bookmark | |||
332 | /** | 342 | /** |
333 | * Set the Tags. | 343 | * Set the Tags. |
334 | * | 344 | * |
335 | * @param array $tags | 345 | * @param string[]|null $tags |
336 | * | 346 | * |
337 | * @return Bookmark | 347 | * @return Bookmark |
338 | */ | 348 | */ |
339 | public function setTags($tags) | 349 | public function setTags(?array $tags): Bookmark |
340 | { | 350 | { |
341 | $this->setTagsString(implode(' ', $tags)); | 351 | $this->setTagsString(implode(' ', $tags ?? [])); |
342 | 352 | ||
343 | return $this; | 353 | return $this; |
344 | } | 354 | } |
@@ -346,7 +356,7 @@ class Bookmark | |||
346 | /** | 356 | /** |
347 | * Get the Thumbnail. | 357 | * Get the Thumbnail. |
348 | * | 358 | * |
349 | * @return string|bool | 359 | * @return string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found |
350 | */ | 360 | */ |
351 | public function getThumbnail() | 361 | public function getThumbnail() |
352 | { | 362 | { |
@@ -356,11 +366,11 @@ class Bookmark | |||
356 | /** | 366 | /** |
357 | * Set the Thumbnail. | 367 | * Set the Thumbnail. |
358 | * | 368 | * |
359 | * @param string|bool $thumbnail | 369 | * @param string|bool|null $thumbnail Thumbnail's URL - false if no thumbnail could be found |
360 | * | 370 | * |
361 | * @return Bookmark | 371 | * @return Bookmark |
362 | */ | 372 | */ |
363 | public function setThumbnail($thumbnail) | 373 | public function setThumbnail($thumbnail): Bookmark |
364 | { | 374 | { |
365 | $this->thumbnail = $thumbnail; | 375 | $this->thumbnail = $thumbnail; |
366 | 376 | ||
@@ -368,11 +378,29 @@ class Bookmark | |||
368 | } | 378 | } |
369 | 379 | ||
370 | /** | 380 | /** |
381 | * Return true if: | ||
382 | * - the bookmark's thumbnail is not already set to false (= not found) | ||
383 | * - it's not a note | ||
384 | * - it's an HTTP(S) link | ||
385 | * - the thumbnail has not yet be retrieved (null) or its associated cache file doesn't exist anymore | ||
386 | * | ||
387 | * @return bool True if the bookmark's thumbnail needs to be retrieved. | ||
388 | */ | ||
389 | public function shouldUpdateThumbnail(): bool | ||
390 | { | ||
391 | return $this->thumbnail !== false | ||
392 | && !$this->isNote() | ||
393 | && startsWith(strtolower($this->url), 'http') | ||
394 | && (null === $this->thumbnail || !is_file($this->thumbnail)) | ||
395 | ; | ||
396 | } | ||
397 | |||
398 | /** | ||
371 | * Get the Sticky. | 399 | * Get the Sticky. |
372 | * | 400 | * |
373 | * @return bool | 401 | * @return bool |
374 | */ | 402 | */ |
375 | public function isSticky() | 403 | public function isSticky(): bool |
376 | { | 404 | { |
377 | return $this->sticky ? true : false; | 405 | return $this->sticky ? true : false; |
378 | } | 406 | } |
@@ -380,11 +408,11 @@ class Bookmark | |||
380 | /** | 408 | /** |
381 | * Set the Sticky. | 409 | * Set the Sticky. |
382 | * | 410 | * |
383 | * @param bool $sticky | 411 | * @param bool|null $sticky |
384 | * | 412 | * |
385 | * @return Bookmark | 413 | * @return Bookmark |
386 | */ | 414 | */ |
387 | public function setSticky($sticky) | 415 | public function setSticky(?bool $sticky): Bookmark |
388 | { | 416 | { |
389 | $this->sticky = $sticky ? true : false; | 417 | $this->sticky = $sticky ? true : false; |
390 | 418 | ||
@@ -394,7 +422,7 @@ class Bookmark | |||
394 | /** | 422 | /** |
395 | * @return string Bookmark's tags as a string, separated by a space | 423 | * @return string Bookmark's tags as a string, separated by a space |
396 | */ | 424 | */ |
397 | public function getTagsString() | 425 | public function getTagsString(): string |
398 | { | 426 | { |
399 | return implode(' ', $this->getTags()); | 427 | return implode(' ', $this->getTags()); |
400 | } | 428 | } |
@@ -402,10 +430,10 @@ class Bookmark | |||
402 | /** | 430 | /** |
403 | * @return bool | 431 | * @return bool |
404 | */ | 432 | */ |
405 | public function isNote() | 433 | public function isNote(): bool |
406 | { | 434 | { |
407 | // We check empty value to get a valid result if the link has not been saved yet | 435 | // We check empty value to get a valid result if the link has not been saved yet |
408 | return empty($this->url) || $this->url[0] === '?'; | 436 | return empty($this->url) || startsWith($this->url, '/shaare/') || $this->url[0] === '?'; |
409 | } | 437 | } |
410 | 438 | ||
411 | /** | 439 | /** |
@@ -415,14 +443,14 @@ class Bookmark | |||
415 | * - multiple spaces will be removed | 443 | * - multiple spaces will be removed |
416 | * - trailing dash in tags will be removed | 444 | * - trailing dash in tags will be removed |
417 | * | 445 | * |
418 | * @param string $tags | 446 | * @param string|null $tags |
419 | * | 447 | * |
420 | * @return $this | 448 | * @return $this |
421 | */ | 449 | */ |
422 | public function setTagsString($tags) | 450 | public function setTagsString(?string $tags): Bookmark |
423 | { | 451 | { |
424 | // Remove first '-' char in tags. | 452 | // Remove first '-' char in tags. |
425 | $tags = preg_replace('/(^| )\-/', '$1', $tags); | 453 | $tags = preg_replace('/(^| )\-/', '$1', $tags ?? ''); |
426 | // Explode all tags separted by spaces or commas | 454 | // Explode all tags separted by spaces or commas |
427 | $tags = preg_split('/[\s,]+/', $tags); | 455 | $tags = preg_split('/[\s,]+/', $tags); |
428 | // Remove eventual empty values | 456 | // Remove eventual empty values |
@@ -434,12 +462,50 @@ class Bookmark | |||
434 | } | 462 | } |
435 | 463 | ||
436 | /** | 464 | /** |
465 | * Get entire additionalContent array. | ||
466 | * | ||
467 | * @return mixed[] | ||
468 | */ | ||
469 | public function getAdditionalContent(): array | ||
470 | { | ||
471 | return $this->additionalContent; | ||
472 | } | ||
473 | |||
474 | /** | ||
475 | * Set a single entry in additionalContent, by key. | ||
476 | * | ||
477 | * @param string $key | ||
478 | * @param mixed|null $value Any type of value can be set. | ||
479 | * | ||
480 | * @return $this | ||
481 | */ | ||
482 | public function addAdditionalContentEntry(string $key, $value): self | ||
483 | { | ||
484 | $this->additionalContent[$key] = $value; | ||
485 | |||
486 | return $this; | ||
487 | } | ||
488 | |||
489 | /** | ||
490 | * Get a single entry in additionalContent, by key. | ||
491 | * | ||
492 | * @param string $key | ||
493 | * @param mixed|null $default | ||
494 | * | ||
495 | * @return mixed|null can be any type or even null. | ||
496 | */ | ||
497 | public function getAdditionalContentEntry(string $key, $default = null) | ||
498 | { | ||
499 | return array_key_exists($key, $this->additionalContent) ? $this->additionalContent[$key] : $default; | ||
500 | } | ||
501 | |||
502 | /** | ||
437 | * Rename a tag in tags list. | 503 | * Rename a tag in tags list. |
438 | * | 504 | * |
439 | * @param string $fromTag | 505 | * @param string $fromTag |
440 | * @param string $toTag | 506 | * @param string $toTag |
441 | */ | 507 | */ |
442 | public function renameTag($fromTag, $toTag) | 508 | public function renameTag(string $fromTag, string $toTag): void |
443 | { | 509 | { |
444 | if (($pos = array_search($fromTag, $this->tags)) !== false) { | 510 | if (($pos = array_search($fromTag, $this->tags)) !== false) { |
445 | $this->tags[$pos] = trim($toTag); | 511 | $this->tags[$pos] = trim($toTag); |
@@ -451,7 +517,7 @@ class Bookmark | |||
451 | * | 517 | * |
452 | * @param string $tag | 518 | * @param string $tag |
453 | */ | 519 | */ |
454 | public function deleteTag($tag) | 520 | public function deleteTag(string $tag): void |
455 | { | 521 | { |
456 | if (($pos = array_search($tag, $this->tags)) !== false) { | 522 | if (($pos = array_search($tag, $this->tags)) !== false) { |
457 | unset($this->tags[$pos]); | 523 | unset($this->tags[$pos]); |