]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/bookmark/Bookmark.php
Merge pull request #1583 from ArthurHoaro/feature/bookmark-strict-types
[github/shaarli/Shaarli.git] / application / bookmark / Bookmark.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Shaarli\Bookmark;
6
7 use DateTime;
8 use DateTimeInterface;
9 use Shaarli\Bookmark\Exception\InvalidBookmarkException;
10
11 /**
12 * Class Bookmark
13 *
14 * This class represent a single Bookmark with all its attributes.
15 * Every bookmark should manipulated using this, before being formatted.
16 *
17 * @package Shaarli\Bookmark
18 */
19 class Bookmark
20 {
21 /** @var string Date format used in string (former ID format) */
22 const LINK_DATE_FORMAT = 'Ymd_His';
23
24 /** @var int Bookmark ID */
25 protected $id;
26
27 /** @var string Permalink identifier */
28 protected $shortUrl;
29
30 /** @var string Bookmark's URL - $shortUrl prefixed with `?` for notes */
31 protected $url;
32
33 /** @var string Bookmark's title */
34 protected $title;
35
36 /** @var string Raw bookmark's description */
37 protected $description;
38
39 /** @var array List of bookmark's tags */
40 protected $tags;
41
42 /** @var string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found */
43 protected $thumbnail;
44
45 /** @var bool Set to true if the bookmark is set as sticky */
46 protected $sticky;
47
48 /** @var DateTimeInterface Creation datetime */
49 protected $created;
50
51 /** @var DateTimeInterface datetime */
52 protected $updated;
53
54 /** @var bool True if the bookmark can only be seen while logged in */
55 protected $private;
56
57 /**
58 * Initialize a link from array data. Especially useful to create a Bookmark from former link storage format.
59 *
60 * @param array $data
61 *
62 * @return $this
63 */
64 public function fromArray(array $data): Bookmark
65 {
66 $this->id = $data['id'] ?? null;
67 $this->shortUrl = $data['shorturl'] ?? null;
68 $this->url = $data['url'] ?? null;
69 $this->title = $data['title'] ?? null;
70 $this->description = $data['description'] ?? null;
71 $this->thumbnail = $data['thumbnail'] ?? null;
72 $this->sticky = $data['sticky'] ?? false;
73 $this->created = $data['created'] ?? null;
74 if (is_array($data['tags'])) {
75 $this->tags = $data['tags'];
76 } else {
77 $this->tags = preg_split('/\s+/', $data['tags'] ?? '', -1, PREG_SPLIT_NO_EMPTY);
78 }
79 if (! empty($data['updated'])) {
80 $this->updated = $data['updated'];
81 }
82 $this->private = ($data['private'] ?? false) ? true : false;
83
84 return $this;
85 }
86
87 /**
88 * Make sure that the current instance of Bookmark is valid and can be saved into the data store.
89 * A valid link requires:
90 * - an integer ID
91 * - a short URL (for permalinks)
92 * - a creation date
93 *
94 * This function also initialize optional empty fields:
95 * - the URL with the permalink
96 * - the title with the URL
97 *
98 * @throws InvalidBookmarkException
99 */
100 public function validate(): void
101 {
102 if ($this->id === null
103 || ! is_int($this->id)
104 || empty($this->shortUrl)
105 || empty($this->created)
106 ) {
107 throw new InvalidBookmarkException($this);
108 }
109 if (empty($this->url)) {
110 $this->url = '/shaare/'. $this->shortUrl;
111 }
112 if (empty($this->title)) {
113 $this->title = $this->url;
114 }
115 }
116
117 /**
118 * Set the Id.
119 * If they're not already initialized, this function also set:
120 * - created: with the current datetime
121 * - shortUrl: with a generated small hash from the date and the given ID
122 *
123 * @param int|null $id
124 *
125 * @return Bookmark
126 */
127 public function setId(?int $id): Bookmark
128 {
129 $this->id = $id;
130 if (empty($this->created)) {
131 $this->created = new DateTime();
132 }
133 if (empty($this->shortUrl)) {
134 $this->shortUrl = link_small_hash($this->created, $this->id);
135 }
136
137 return $this;
138 }
139
140 /**
141 * Get the Id.
142 *
143 * @return int|null
144 */
145 public function getId(): ?int
146 {
147 return $this->id;
148 }
149
150 /**
151 * Get the ShortUrl.
152 *
153 * @return string|null
154 */
155 public function getShortUrl(): ?string
156 {
157 return $this->shortUrl;
158 }
159
160 /**
161 * Get the Url.
162 *
163 * @return string|null
164 */
165 public function getUrl(): ?string
166 {
167 return $this->url;
168 }
169
170 /**
171 * Get the Title.
172 *
173 * @return string
174 */
175 public function getTitle(): ?string
176 {
177 return $this->title;
178 }
179
180 /**
181 * Get the Description.
182 *
183 * @return string
184 */
185 public function getDescription(): string
186 {
187 return ! empty($this->description) ? $this->description : '';
188 }
189
190 /**
191 * Get the Created.
192 *
193 * @return DateTimeInterface
194 */
195 public function getCreated(): ?DateTimeInterface
196 {
197 return $this->created;
198 }
199
200 /**
201 * Get the Updated.
202 *
203 * @return DateTimeInterface
204 */
205 public function getUpdated(): ?DateTimeInterface
206 {
207 return $this->updated;
208 }
209
210 /**
211 * Set the ShortUrl.
212 *
213 * @param string|null $shortUrl
214 *
215 * @return Bookmark
216 */
217 public function setShortUrl(?string $shortUrl): Bookmark
218 {
219 $this->shortUrl = $shortUrl;
220
221 return $this;
222 }
223
224 /**
225 * Set the Url.
226 *
227 * @param string|null $url
228 * @param string[] $allowedProtocols
229 *
230 * @return Bookmark
231 */
232 public function setUrl(?string $url, array $allowedProtocols = []): Bookmark
233 {
234 $url = $url !== null ? trim($url) : '';
235 if (! empty($url)) {
236 $url = whitelist_protocols($url, $allowedProtocols);
237 }
238 $this->url = $url;
239
240 return $this;
241 }
242
243 /**
244 * Set the Title.
245 *
246 * @param string|null $title
247 *
248 * @return Bookmark
249 */
250 public function setTitle(?string $title): Bookmark
251 {
252 $this->title = $title !== null ? trim($title) : '';
253
254 return $this;
255 }
256
257 /**
258 * Set the Description.
259 *
260 * @param string|null $description
261 *
262 * @return Bookmark
263 */
264 public function setDescription(?string $description): Bookmark
265 {
266 $this->description = $description;
267
268 return $this;
269 }
270
271 /**
272 * Set the Created.
273 * Note: you shouldn't set this manually except for special cases (like bookmark import)
274 *
275 * @param DateTimeInterface|null $created
276 *
277 * @return Bookmark
278 */
279 public function setCreated(?DateTimeInterface $created): Bookmark
280 {
281 $this->created = $created;
282
283 return $this;
284 }
285
286 /**
287 * Set the Updated.
288 *
289 * @param DateTimeInterface|null $updated
290 *
291 * @return Bookmark
292 */
293 public function setUpdated(?DateTimeInterface $updated): Bookmark
294 {
295 $this->updated = $updated;
296
297 return $this;
298 }
299
300 /**
301 * Get the Private.
302 *
303 * @return bool
304 */
305 public function isPrivate(): bool
306 {
307 return $this->private ? true : false;
308 }
309
310 /**
311 * Set the Private.
312 *
313 * @param bool|null $private
314 *
315 * @return Bookmark
316 */
317 public function setPrivate(?bool $private): Bookmark
318 {
319 $this->private = $private ? true : false;
320
321 return $this;
322 }
323
324 /**
325 * Get the Tags.
326 *
327 * @return string[]
328 */
329 public function getTags(): array
330 {
331 return is_array($this->tags) ? $this->tags : [];
332 }
333
334 /**
335 * Set the Tags.
336 *
337 * @param string[]|null $tags
338 *
339 * @return Bookmark
340 */
341 public function setTags(?array $tags): Bookmark
342 {
343 $this->setTagsString(implode(' ', $tags ?? []));
344
345 return $this;
346 }
347
348 /**
349 * Get the Thumbnail.
350 *
351 * @return string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found
352 */
353 public function getThumbnail()
354 {
355 return !$this->isNote() ? $this->thumbnail : false;
356 }
357
358 /**
359 * Set the Thumbnail.
360 *
361 * @param string|bool|null $thumbnail Thumbnail's URL - false if no thumbnail could be found
362 *
363 * @return Bookmark
364 */
365 public function setThumbnail($thumbnail): Bookmark
366 {
367 $this->thumbnail = $thumbnail;
368
369 return $this;
370 }
371
372 /**
373 * Get the Sticky.
374 *
375 * @return bool
376 */
377 public function isSticky(): bool
378 {
379 return $this->sticky ? true : false;
380 }
381
382 /**
383 * Set the Sticky.
384 *
385 * @param bool|null $sticky
386 *
387 * @return Bookmark
388 */
389 public function setSticky(?bool $sticky): Bookmark
390 {
391 $this->sticky = $sticky ? true : false;
392
393 return $this;
394 }
395
396 /**
397 * @return string Bookmark's tags as a string, separated by a space
398 */
399 public function getTagsString(): string
400 {
401 return implode(' ', $this->getTags());
402 }
403
404 /**
405 * @return bool
406 */
407 public function isNote(): bool
408 {
409 // We check empty value to get a valid result if the link has not been saved yet
410 return empty($this->url) || startsWith($this->url, '/shaare/') || $this->url[0] === '?';
411 }
412
413 /**
414 * Set tags from a string.
415 * Note:
416 * - tags must be separated whether by a space or a comma
417 * - multiple spaces will be removed
418 * - trailing dash in tags will be removed
419 *
420 * @param string|null $tags
421 *
422 * @return $this
423 */
424 public function setTagsString(?string $tags): Bookmark
425 {
426 // Remove first '-' char in tags.
427 $tags = preg_replace('/(^| )\-/', '$1', $tags ?? '');
428 // Explode all tags separted by spaces or commas
429 $tags = preg_split('/[\s,]+/', $tags);
430 // Remove eventual empty values
431 $tags = array_values(array_filter($tags));
432
433 $this->tags = $tags;
434
435 return $this;
436 }
437
438 /**
439 * Rename a tag in tags list.
440 *
441 * @param string $fromTag
442 * @param string $toTag
443 */
444 public function renameTag(string $fromTag, string $toTag): void
445 {
446 if (($pos = array_search($fromTag, $this->tags)) !== false) {
447 $this->tags[$pos] = trim($toTag);
448 }
449 }
450
451 /**
452 * Delete a tag from tags list.
453 *
454 * @param string $tag
455 */
456 public function deleteTag(string $tag): void
457 {
458 if (($pos = array_search($tag, $this->tags)) !== false) {
459 unset($this->tags[$pos]);
460 $this->tags = array_values($this->tags);
461 }
462 }
463 }