3 declare(strict_types
=1);
5 namespace Shaarli\Bookmark
;
7 use Shaarli\Bookmark\Exception\InvalidBookmarkException
;
12 * Implementing ArrayAccess, this allows us to use the bookmark list
13 * as an array and iterate over it.
15 * @package Shaarli\Bookmark
17 class BookmarkArray
implements \Iterator
, \Countable
, \ArrayAccess
25 * @var array List of all bookmarks IDS mapped with their array offset.
31 * @var int Position in the $this->keys array (for the Iterator interface)
36 * @var array List of offset keys (for the Iterator interface implementation)
41 * @var array List of all recorded URLs (key=url, value=bookmark offset)
42 * for fast reserve search (url-->bookmark offset)
46 public function __construct()
49 $this->bookmarks
= [];
56 * Countable - Counts elements of an object
58 * @return int Number of bookmarks
60 public function count()
62 return count($this->bookmarks
);
66 * ArrayAccess - Assigns a value to the specified offset
68 * @param int $offset Bookmark ID
69 * @param Bookmark $value instance
71 * @throws InvalidBookmarkException
73 public function offsetSet($offset, $value)
75 if (! $value instanceof Bookmark
76 || $value->getId() === null || empty($value->getUrl())
77 || ($offset !== null && ! is_int($offset)) || ! is_int($value->getId())
78 || $offset !== null && $offset !== $value->getId()
80 throw new InvalidBookmarkException($value);
83 // If the bookmark exists, we reuse the real offset, otherwise new entry
84 if ($offset !== null) {
85 $existing = $this->getBookmarkOffset($offset);
87 $existing = $this->getBookmarkOffset($value->getId());
90 if ($existing !== null) {
93 $offset = count($this->bookmarks
);
96 $this->bookmarks
[$offset] = $value;
97 $this->urls
[$value->getUrl()] = $offset;
98 $this->ids
[$value->getId()] = $offset;
102 * ArrayAccess - Whether or not an offset exists
104 * @param int $offset Bookmark ID
106 * @return bool true if it exists, false otherwise
108 public function offsetExists($offset)
110 return array_key_exists($this->getBookmarkOffset($offset), $this->bookmarks
);
114 * ArrayAccess - Unsets an offset
116 * @param int $offset Bookmark ID
118 public function offsetUnset($offset)
120 $realOffset = $this->getBookmarkOffset($offset);
121 $url = $this->bookmarks
[$realOffset]->getUrl();
122 unset($this->urls
[$url]);
123 unset($this->ids
[$offset]);
124 unset($this->bookmarks
[$realOffset]);
128 * ArrayAccess - Returns the value at specified offset
130 * @param int $offset Bookmark ID
132 * @return Bookmark|null The Bookmark if found, null otherwise
134 public function offsetGet($offset)
136 $realOffset = $this->getBookmarkOffset($offset);
137 return isset($this->bookmarks
[$realOffset]) ? $this->bookmarks
[$realOffset] : null;
141 * Iterator - Returns the current element
143 * @return Bookmark corresponding to the current position
145 public function current()
147 return $this[$this->keys
[$this->position
]];
151 * Iterator - Returns the key of the current element
153 * @return int Bookmark ID corresponding to the current position
155 public function key()
157 return $this->keys
[$this->position
];
161 * Iterator - Moves forward to next element
163 public function next()
169 * Iterator - Rewinds the Iterator to the first element
171 * Entries are sorted by date (latest first)
173 public function rewind()
175 $this->keys
= array_keys($this->ids
);
180 * Iterator - Checks if current position is valid
182 * @return bool true if the current Bookmark ID exists, false otherwise
184 public function valid()
186 return isset($this->keys
[$this->position
]);
190 * Returns a bookmark offset in bookmarks array from its unique ID.
192 * @param int|null $id Persistent ID of a bookmark.
194 * @return int Real offset in local array, or null if doesn't exist.
196 protected function getBookmarkOffset(?int $id): ?int
198 if ($id !== null && isset($this->ids
[$id])) {
199 return $this->ids
[$id];
205 * Return the next key for bookmark creation.
206 * E.g. If the last ID is 597, the next will be 598.
208 * @return int next ID.
210 public function getNextId(): int
212 if (!empty($this->ids
)) {
213 return max(array_keys($this->ids
)) +
1;
221 * @return Bookmark|null
223 public function getByUrl(string $url): ?Bookmark
226 && isset($this->urls
[$url])
227 && isset($this->bookmarks
[$this->urls
[$url]])
229 return $this->bookmarks
[$this->urls
[$url]];
235 * Reorder links by creation date (newest first).
237 * Also update the urls and ids mapping arrays.
239 * @param string $order ASC|DESC
240 * @param bool $ignoreSticky If set to true, sticky bookmarks won't be first
242 public function reorder(string $order = 'DESC', bool $ignoreSticky = false): void
244 $order = $order === 'ASC' ? -1 : 1;
245 // Reorder array by dates.
246 usort($this->bookmarks
, function ($a, $b) use ($order, $ignoreSticky) {
247 /** @var $a Bookmark */
248 /** @var $b Bookmark */
249 if (false === $ignoreSticky && $a->isSticky() !== $b->isSticky()) {
250 return $a->isSticky() ? -1 : 1;
252 return $a->getCreated() < $b->getCreated() ? 1 * $order : -1 * $order;
257 foreach ($this->bookmarks
as $key => $bookmark) {
258 $this->urls
[$bookmark->getUrl()] = $key;
259 $this->ids
[$bookmark->getId()] = $key;