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)
76 ! $value instanceof Bookmark
77 || $value->getId() === null || empty($value->getUrl())
78 || ($offset !== null && ! is_int($offset)) || ! is_int($value->getId())
79 || $offset !== null && $offset !== $value->getId()
81 throw new InvalidBookmarkException($value);
84 // If the bookmark exists, we reuse the real offset, otherwise new entry
85 if ($offset !== null) {
86 $existing = $this->getBookmarkOffset($offset);
88 $existing = $this->getBookmarkOffset($value->getId());
91 if ($existing !== null) {
94 $offset = count($this->bookmarks
);
97 $this->bookmarks
[$offset] = $value;
98 $this->urls
[$value->getUrl()] = $offset;
99 $this->ids
[$value->getId()] = $offset;
103 * ArrayAccess - Whether or not an offset exists
105 * @param int $offset Bookmark ID
107 * @return bool true if it exists, false otherwise
109 public function offsetExists($offset)
111 return array_key_exists($this->getBookmarkOffset($offset), $this->bookmarks
);
115 * ArrayAccess - Unsets an offset
117 * @param int $offset Bookmark ID
119 public function offsetUnset($offset)
121 $realOffset = $this->getBookmarkOffset($offset);
122 $url = $this->bookmarks
[$realOffset]->getUrl();
123 unset($this->urls
[$url]);
124 unset($this->ids
[$offset]);
125 unset($this->bookmarks
[$realOffset]);
129 * ArrayAccess - Returns the value at specified offset
131 * @param int $offset Bookmark ID
133 * @return Bookmark|null The Bookmark if found, null otherwise
135 public function offsetGet($offset)
137 $realOffset = $this->getBookmarkOffset($offset);
138 return isset($this->bookmarks
[$realOffset]) ? $this->bookmarks
[$realOffset] : null;
142 * Iterator - Returns the current element
144 * @return Bookmark corresponding to the current position
146 public function current()
148 return $this[$this->keys
[$this->position
]];
152 * Iterator - Returns the key of the current element
154 * @return int Bookmark ID corresponding to the current position
156 public function key()
158 return $this->keys
[$this->position
];
162 * Iterator - Moves forward to next element
164 public function next()
170 * Iterator - Rewinds the Iterator to the first element
172 * Entries are sorted by date (latest first)
174 public function rewind()
176 $this->keys
= array_keys($this->ids
);
181 * Iterator - Checks if current position is valid
183 * @return bool true if the current Bookmark ID exists, false otherwise
185 public function valid()
187 return isset($this->keys
[$this->position
]);
191 * Returns a bookmark offset in bookmarks array from its unique ID.
193 * @param int|null $id Persistent ID of a bookmark.
195 * @return int Real offset in local array, or null if doesn't exist.
197 protected function getBookmarkOffset(?int $id): ?int
199 if ($id !== null && isset($this->ids
[$id])) {
200 return $this->ids
[$id];
206 * Return the next key for bookmark creation.
207 * E.g. If the last ID is 597, the next will be 598.
209 * @return int next ID.
211 public function getNextId(): int
213 if (!empty($this->ids
)) {
214 return max(array_keys($this->ids
)) +
1;
222 * @return Bookmark|null
224 public function getByUrl(string $url): ?Bookmark
228 && isset($this->urls
[$url])
229 && isset($this->bookmarks
[$this->urls
[$url]])
231 return $this->bookmarks
[$this->urls
[$url]];
237 * Reorder links by creation date (newest first).
239 * Also update the urls and ids mapping arrays.
241 * @param string $order ASC|DESC
242 * @param bool $ignoreSticky If set to true, sticky bookmarks won't be first
244 public function reorder(string $order = 'DESC', bool $ignoreSticky = false): void
246 $order = $order === 'ASC' ? -1 : 1;
247 // Reorder array by dates.
248 usort($this->bookmarks
, function ($a, $b) use ($order, $ignoreSticky) {
249 /** @var $a Bookmark */
250 /** @var $b Bookmark */
251 if (false === $ignoreSticky && $a->isSticky() !== $b->isSticky()) {
252 return $a->isSticky() ? -1 : 1;
254 return $a->getCreated() < $b->getCreated() ? 1 * $order : -1 * $order;
259 foreach ($this->bookmarks
as $key => $bookmark) {
260 $this->urls
[$bookmark->getUrl()] = $key;
261 $this->ids
[$bookmark->getId()] = $key;