Controller
in package
Route handlers for the front controller (index.php). Each handler deals with one route and returns a `[template, variables]` pair to render, or redirects and exits directly.
Table of Contents
Methods
-
add()
: array{0: string, 1: array
} - Add bookmark (POST action): validate and create it, then return to the list the form was on. A GET redirects to the user's own page with the inline add form open (`?add`).
-
delete()
: array{0: string, 1: array
} - Delete bookmark (POST action, the Delete button of the inline edit form), then return to the list the form was on.
-
edit()
: array{0: string, 1: array
} - Edit bookmark (POST action): validate and save it, then return to the list the form was on. A GET redirects to the user's own page with that row's inline edit form open (`?edit=ID`).
-
home()
: array{0: string, 1: array
} - Home page: one page of the bookmark feed (`?page=N`) and the tag cloud.
-
login()
: array{0: string, 1: array
} - Login: show the form (GET) or process it (POST).
- logout() : never
- Logout: destroy the session and redirect to the home page.
-
notFound()
: array{0: string, 1: array
} - Not found: send a 404 status and render the not-found view.
- redirect() : never
- Send a redirect to the given path and stop the request.
-
tag()
: array{0: string, 1: array
} - Tag page: the feed filtered by tag names (`/tag/NAME` or `/tag/NAME,NAME,...`, comma-separated: bookmarks carrying ALL of them), paginated.
- tagError() : string|null
- Validate a bookmark's normalised tag names.
-
tags()
: array{0: string, 1: array
} - Tag index page (`/tags`): every visible tag with its count, paginated and alphabetical; `?q=TERM` narrows it to the matching tags. The list context travels along (`?tag=NAMES` and/or `?user=NAME`), scoping the index/search to that list, exactly like the cloud.
-
user()
: array{0: string, 1: array
} - User page: the feed filtered by one username (`/user/USERNAME`), paginated.
- validate() : array<string, string>
- Validate a bookmark's user-supplied fields.
- apply() : array<string, string>
- Fill a bookmark from form input and validate it (CSRF included).
-
feed()
: array{0: string, 1: array
} - The variables every list view shares. Inline form state comes from the query string: `?edit=ID` renders that row as an in-place edit form (the template still gates it with `Auth::canManage`) and `?add` renders an empty form on top of the list. Both are view states of the list, so they live in the query string; mutations POST to the `/add`, `/edit/ID` and `/delete/ID` action routes.
- isValidLink() : bool
- Whether a string is an acceptable bookmark URL.
-
listFromReturn()
: array{0: string, 1: array
} - Rebuild the list view the submitted form was on (see `returnPath()`), to re-render it with the inline form when validation fails.
- ownPage() : string
- The logged-in user's own list page (`user/USERNAME`).
- page() : int
- The requested list page number (`?page=N`, 1-based).
- returnPath() : string
- The list the submitted form was on, as a safe relative path: the form's hidden `return` field, accepted only when it matches a known list route plus an optional page (never an absolute URL, so it cannot be turned into an open redirect). Falls back to the user's own page.
- tagNames() : array<int, string>
- Parse a comma-separated tag list into the canonical tag-name set: a comma can never be part of a tag (it is the input separator), so the names are split on it, then trimmed, lower-cased, deduplicated and sorted.
- tagRoute() : string
- The list route for a set of tag names (`tag/NAME,NAME`).
Methods
add()
Add bookmark (POST action): validate and create it, then return to the list the form was on. A GET redirects to the user's own page with the inline add form open (`?add`).
public
static add() : array{0: string, 1: array}
Requires a logged-in user. On invalid input the origin list is re-rendered with the inline form, the typed values and the errors.
Return values
array{0: string, 1: arrayTemplate name and its variables.
delete()
Delete bookmark (POST action, the Delete button of the inline edit form), then return to the list the form was on.
public
static delete(string $id) : array{0: string, 1: array}
Requires a logged-in user who may manage the bookmark and a valid CSRF token; anything else (including GET requests) renders the 404 view.
Parameters
- $id : string
-
Bookmark id from the URL (second path segment).
Return values
array{0: string, 1: arrayTemplate name and its variables.
edit()
Edit bookmark (POST action): validate and save it, then return to the list the form was on. A GET redirects to the user's own page with that row's inline edit form open (`?edit=ID`).
public
static edit(string $id) : array{0: string, 1: array}
Requires a logged-in user who may manage the bookmark; a missing id or a foreign bookmark renders the 404 view (existence is not revealed). On invalid input the origin list is re-rendered with the inline form, the typed values and the errors.
Parameters
- $id : string
-
Bookmark id from the URL (second path segment).
Return values
array{0: string, 1: arrayTemplate name and its variables.
home()
Home page: one page of the bookmark feed (`?page=N`) and the tag cloud.
public
static home() : array{0: string, 1: array}
Return values
array{0: string, 1: arrayTemplate name and its variables.
login()
Login: show the form (GET) or process it (POST).
public
static login() : array{0: string, 1: array}
On a valid POST (CSRF token plus credentials) the user is logged in and
redirected to their own page. Otherwise the form is shown, with an
error flag after a failed attempt.
Return values
array{0: string, 1: arrayTemplate name and its variables.
logout()
Logout: destroy the session and redirect to the home page.
public
static logout() : never
Return values
nevernotFound()
Not found: send a 404 status and render the not-found view.
public
static notFound() : array{0: string, 1: array}
Return values
array{0: string, 1: arrayTemplate name and its variables.
redirect()
Send a redirect to the given path and stop the request.
public
static redirect(string $path) : never
Parameters
- $path : string
-
Absolute URL path to redirect to.
Return values
nevertag()
Tag page: the feed filtered by tag names (`/tag/NAME` or `/tag/NAME,NAME,...`, comma-separated: bookmarks carrying ALL of them), paginated.
public
static tag(string $segment) : array{0: string, 1: array}
An unknown tag renders an empty page rather than a 404, so the response does not reveal whether a (possibly private) tag exists.
Parameters
- $segment : string
-
Tag name(s) from the URL (second path segment, encoded).
Return values
array{0: string, 1: arrayTemplate name and its variables.
tagError()
Validate a bookmark's normalised tag names.
public
static tagError(array<int, string> $names) : string|null
Parameters
- $names : array<int, string>
-
Parsed tag names (see
Bookmark::parseTags()).
Return values
string|null —An error message, or null when the tags are fine.
tags()
Tag index page (`/tags`): every visible tag with its count, paginated and alphabetical; `?q=TERM` narrows it to the matching tags. The list context travels along (`?tag=NAMES` and/or `?user=NAME`), scoping the index/search to that list, exactly like the cloud.
public
static tags() : array{0: string, 1: array}
Return values
array{0: string, 1: arrayTemplate name and its variables.
user()
User page: the feed filtered by one username (`/user/USERNAME`), paginated.
public
static user(string $name) : array{0: string, 1: array}
/user/me is a reserved alias (see User::RESERVED) that redirects to
the logged-in user's own page. An unknown username renders an empty
page rather than a 404, so the response does not reveal whether an
account exists.
Parameters
- $name : string
-
Username from the URL (second path segment, encoded).
Return values
array{0: string, 1: arrayTemplate name and its variables.
validate()
Validate a bookmark's user-supplied fields.
public
static validate(Bookmark $bookmark) : array<string, string>
Parameters
- $bookmark : Bookmark
-
The bookmark to check (already filled).
Return values
array<string, string> —Error message per invalid field; empty if valid.
apply()
Fill a bookmark from form input and validate it (CSRF included).
private
static apply(Bookmark $bookmark, array<string, mixed> $input) : array<string, string>
Parameters
- $bookmark : Bookmark
-
The bookmark to fill.
- $input : array<string, mixed>
-
The submitted form fields (
$_POST).
Return values
array<string, string> —Error message per invalid field; empty if valid.
feed()
The variables every list view shares. Inline form state comes from the query string: `?edit=ID` renders that row as an in-place edit form (the template still gates it with `Auth::canManage`) and `?add` renders an empty form on top of the list. Both are view states of the list, so they live in the query string; mutations POST to the `/add`, `/edit/ID` and `/delete/ID` action routes.
private
static feed(int $page, array<string|int, mixed> $tagNames, string|null $userName, string $route) : array{0: string, 1: array}
Parameters
- $page : int
- $tagNames : array<string|int, mixed>
- $userName : string|null
- $route : string
Return values
array{0: string, 1: arrayTemplate name and its variables.
isValidLink()
Whether a string is an acceptable bookmark URL.
private
static isValidLink(string $url) : bool
Accepts only http/https, requires a host, and rejects embedded
whitespace/control characters and userinfo (user:pass@, a phishing
vector). Host shape is left permissive on purpose (IPs, localhost and
intranet hosts are valid targets for a self-hosted instance).
Parameters
- $url : string
-
The URL to check.
Return values
boollistFromReturn()
Rebuild the list view the submitted form was on (see `returnPath()`), to re-render it with the inline form when validation fails.
private
static listFromReturn() : array{0: string, 1: array}
Return values
array{0: string, 1: arrayTemplate name and its variables.
ownPage()
The logged-in user's own list page (`user/USERNAME`).
private
static ownPage() : string
Return values
stringpage()
The requested list page number (`?page=N`, 1-based).
private
static page() : int
Return values
intreturnPath()
The list the submitted form was on, as a safe relative path: the form's hidden `return` field, accepted only when it matches a known list route plus an optional page (never an absolute URL, so it cannot be turned into an open redirect). Falls back to the user's own page.
private
static returnPath() : string
Return values
stringtagNames()
Parse a comma-separated tag list into the canonical tag-name set: a comma can never be part of a tag (it is the input separator), so the names are split on it, then trimmed, lower-cased, deduplicated and sorted.
private
static tagNames(string $list) : array<int, string>
Parameters
- $list : string
-
The comma-separated tag names (already decoded).
Return values
array<int, string> —The tag names; empty if none survive.
tagRoute()
The list route for a set of tag names (`tag/NAME,NAME`).
private
static tagRoute(array<int, string> $names) : string
Parameters
- $names : array<int, string>
-
The tag names.