['scope' => 'user'] + $note, $userId > 0 ? $this->list($userId) : []), ...array_map(static fn (array $note): array => ['scope' => 'site'] + $note, $this->list(0)), ]; $query = trim($query); if ($query !== '') { $notes = array_filter($notes, static fn (array $note): bool => mb_stripos($note['content'], $query) !== false); } usort($notes, static fn (array $a, array $b): int => $b['id'] <=> $a['id']); return array_map(static fn (array $note): array => [ 'id' => $note['id'], 'scope' => $note['scope'], 'content' => $note['content'], 'created_at' => $note['created_at'], ], array_slice($notes, 0, max(1, min(self::MAX_NOTES, $limit)))); } public function save(int $userId, string $content, string $scope = 'user', int $sessionId = 0): int { $ref = $scope === 'site' ? 0 : $userId; $id = (int) $this->rows->get(self::COUNTER, 0, 0) + 1; $this->rows->put(self::COUNTER, $id, 'counter'); $notes = $this->list($ref); array_unshift($notes, [ 'id' => $id, 'content' => mb_substr(trim($content), 0, self::MAX_LENGTH), 'created_at' => gmdate('Y-m-d H:i:s'), 'session' => $sessionId, 'author' => $userId, ]); $this->rows->put(self::ROW, array_slice($notes, 0, self::MAX_NOTES), 'note', $ref); return $id; } public function forget(int $userId, int $id): bool { return $userId > 0 && $this->remove($userId, $id); } /** * Forget a site note (people who manage MazeGraph only; the caller checks). * * @since 0.1.0 */ public function forgetSite(int $id): bool { return $this->remove(0, $id); } public function forgetUser(int $userId): int { if ($userId <= 0) { return 0; } $count = count($this->list($userId)); $this->rows->delete(self::ROW, $userId); return $count; } /** * The person's own notes, newest first (privacy export). * * @since 0.1.0 * @return list */ public function ofUser(int $userId): array { return array_map(static fn (array $note): array => ['id' => $note['id'], 'content' => $note['content'], 'created_at' => $note['created_at']], $userId > 0 ? $this->list($userId) : []); } /** * @return list */ protected function list(int $ref): array { $stored = $this->rows->get(self::ROW, $ref, []); $notes = []; foreach (is_array($stored) ? $stored : [] as $note) { if (is_array($note) && isset($note['id'], $note['content'])) { $notes[] = [ 'id' => (int) $note['id'], 'content' => (string) $note['content'], 'created_at' => (string) ($note['created_at'] ?? ''), 'session' => (int) ($note['session'] ?? 0), 'author' => (int) ($note['author'] ?? $ref), ]; } } return $notes; } protected function remove(int $ref, int $id): bool { $notes = $this->list($ref); $kept = array_values(array_filter($notes, static fn (array $note): bool => $note['id'] !== $id)); if (count($kept) === count($notes)) { return false; } $kept === [] ? $this->rows->delete(self::ROW, $ref) : $this->rows->put(self::ROW, $kept, 'note', $ref); return true; } }