Zoo

← back to the zoo

5. Find a friend

Any app can search for people by name, including people who have never used it.

rc_user_search($q) finds people by the start of their username or of any word of their name, across the whole platform.

This is the code serving the zoo right now: read from disk for this request, from commit 0c1c124a02 (live). The zoo's own self-check fetches this page and compares it byte for byte with the file it runs.

exhibits/05-find-a-friend.php sha256 d8ec5a3ed97c · raw

1<?php
2// Exhibit 5. The directory is open to every app, so a brand-new app can find people who have
3// never used it. The zoo only searches for signed-in people, so it is not an open directory
4// for the whole internet.
5return [
6    "n" => 5, "try" => "find a friend", "wing" => "Identity",
7    "title" => "Find a friend",
8    "promise" => "Any app can search for people by name, including people who have never used it.",
9    "block" => "rc_user_search(\$q) finds people by the start of their username or of any word of their name, across the whole platform.",
10    "show" => function (?array $me): string {
11        if (!$me) return '<p class="muted"><a href="' . h(rc_signin_url()) . '">Sign in</a> to search for people.</p>';
12        return '<form class="row" id="find"><input name="q" placeholder="A name or username" autocomplete="off" required><button>Search</button></form><div class="people" id="found"></div>';
13    },
14    "api" => function (string $do, ?array $me, array $in, bool $post): ?array {
15        if ($do !== "search") return null;
16        if (!$me) return ["error" => "sign in to search", "status" => 401];
17        return ["users" => rc_user_search((string)($in["q"] ?? ""), 12)];
18    },
19    // rc-check is root's robot: it has an account but has never used the zoo.
20    "check" => function (): array {
21        $hits = array_column(rc_user_search("rc-check", 5), "username");
22        if (!in_array("rc-check", $hits, true)) return [false, "searching \"rc-check\" did not find root's robot, who has an account" . ($hits ? " (found: " . implode(", ", $hits) . ")" : "")];
23        return [true, "found @rc-check, who has an account but has never used the zoo"];
24    },
25    "script" => <<<'JS'
26document.getElementById("find")?.addEventListener("submit", async (ev) => {
27  ev.preventDefault();
28  const out = document.getElementById("found");
29  const j = await zoo.call("/api/5/search?q=" + encodeURIComponent(ev.target.q.value));
30  out.innerHTML = j.error ? zoo.esc(j.error) : (j.users.length ? j.users.map(zoo.person).join("") : '<p class="muted">Nobody by that name.</p>');
31});
32
33JS,
34];