Author: AllenJB (AllenJB) Committer: GitHub (web-flow) Pusher: derickr Date: 2025-11-04T17:17:06Z
Commit: https://github.com/php/web-php/commit/658fafec3eefa3f4d10846fc3ca5c887879fa7a1 Raw diff: https://github.com/php/web-php/commit/658fafec3eefa3f4d10846fc3ca5c887879fa7a1.diff Update search to use new pre-combined indexes + local docs support (#1586) * Update search.js to use new pre-combined indexes; Support for running search on local docs * Update search.js to use new pre-combined indexes; Support for running search on local docs Changed paths: M js/search-index.php M js/search.js Diff: diff --git a/js/search-index.php b/js/search-index.php index 9e76196dff..cfee19d506 100644 --- a/js/search-index.php +++ b/js/search-index.php @@ -11,28 +11,9 @@ header("Location: http://php.net"); } -/* -$types = array( - "phpdoc:varentry", - "refentry", - "phpdoc:exceptionref", - "phpdoc:classref", - "section", - "chapter", - "book", - "reference", - "set", - "appendix", - "article", -); - */ +$combinedIndex = $_SERVER["DOCUMENT_ROOT"] . "/manual/$lang/search-combined.json"; +$tsstring = gmdate("D, d M Y H:i:s ", filemtime($combinedIndex)) . "GMT"; -$indexfile = $_SERVER["DOCUMENT_ROOT"] . "/manual/$lang/search-index.json"; -$descfile = $_SERVER["DOCUMENT_ROOT"] . "/manual/$lang/search-description.json"; - -/* {{{ Cache this */ -$time = max(filemtime($indexfile), filemtime($descfile)); -$tsstring = gmdate("D, d M Y H:i:s ", $time) . "GMT"; if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && ($_SERVER["HTTP_IF_MODIFIED_SINCE"] == $tsstring)) { header("HTTP/1.1 304 Not Modified"); @@ -41,26 +22,4 @@ header("Last-Modified: " . $tsstring); header("Content-Type: application/javascript"); -/* }}} */ - -$s = file_get_contents($indexfile); -$js = json_decode($s, true); - -$index = []; -foreach ($js as $item) { - if ($item[0]) { - /* key: ID/filename, 0=>*/ - $index[$item[1]] = [$item[0], "", $item[2]]; - } -} - -$s = file_get_contents($descfile); -$js = json_decode($s, true); - -foreach ($js as $k => $item) { - if ($item && isset($index[$k])) { - $index[$k][1] = $item; - } -} - -echo json_encode($index); +readfile($combinedIndex); diff --git a/js/search.js b/js/search.js index 69248837b6..807360ca99 100644 --- a/js/search.js +++ b/js/search.js @@ -11,78 +11,21 @@ const initPHPSearch = async (language) => { const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; const CACHE_DAYS = 14; - /** - * Converts the structure from search-index.php into an array of objects, - * mapping the index entries to their respective types. - * - * @param {object} index - * @returns {Array} - */ - const processIndex = (index) => { - return Object.entries(index) - .map(([id, [name, description, tag]]) => { - if (!name) return null; - - let type = "General"; - switch (tag) { - case "phpdoc:varentry": - type = "Variable"; - break; - - case "refentry": - type = "Function"; - break; - - case "phpdoc:exceptionref": - type = "Exception"; - break; - - case "phpdoc:classref": - type = "Class"; - break; - - case "set": - case "book": - case "reference": - type = "Extension"; - break; - } - - return { - id, - name, - description, - tag, - type, - methodName: name.split("::").pop(), - }; - }) - .filter(Boolean); - }; - /** * Looks up the search index cached in localStorage. * * @returns {Array|null} */ const lookupIndexCache = () => { - const key = `search-${language}`; + const key = `search2-${language}`; const cache = window.localStorage.getItem(key); - if (!cache) { + if ((!cache) || (language === 'local')) { return null; } const { data, time: cachedDate } = JSON.parse(cache); - // Invalidate old search cache format (previously an object) - // TODO: Remove this check once the new search index (a single array) - // has been in use for a while. - if (!Array.isArray(data)) { - console.log("Invalidating old search cache format"); - return null; - } - const expireDate = cachedDate + CACHE_DAYS * MILLISECONDS_PER_DAY; if (Date.now() > expireDate) { @@ -98,23 +41,27 @@ const initPHPSearch = async (language) => { * @returns {Promise<Array>} The search index. */ const fetchIndex = async () => { - const key = `search-${language}`; - const response = await fetch(`/js/search-index.php?lang=${language}`); - const data = await response.json(); - const items = processIndex(data); - - try { - localStorage.setItem( - key, - JSON.stringify({ - data: items, - time: Date.now(), - }), - ); - } catch (e) { - // Local storage might be full, or other error. - // Just continue without caching. - console.error("Failed to cache search index", e); + const key = `search2-${language}`; + let items; + if (language === 'local') { + items = localSearchIndexes; + } else { + const response = await fetch(`/js/search-index.php?lang=${language}`); + items = await response.json(); + + try { + localStorage.setItem( + key, + JSON.stringify({ + data: items, + time: Date.now(), + }), + ); + } catch (e) { + // Local storage might be full, or other error. + // Just continue without caching. + console.error("Failed to cache search index", e); + } } return items; @@ -139,7 +86,7 @@ const initPHPSearch = async (language) => { try { return await loadIndex(); } catch (error) { - if (language !== "en") { + if ((language !== "en") && (language !== "local")) { language = "en"; return loadIndexWithFallback(); } @@ -200,7 +147,7 @@ const initSearchModal = () => { const inputElement = document.getElementById("search-modal__input"); const focusTrapHandler = (event) => { - if (event.key != "Tab") { + if (event.key !== "Tab") { return; } @@ -276,19 +223,25 @@ const initSearchModal = () => { "navbar__search-button-mobile", ); const searchButton = document.getElementById("navbar__search-button"); + let buttons = [searchButton]; // Enhance mobile search - searchLink.setAttribute("hidden", "true"); - searchButtonMobile.removeAttribute("hidden"); + if (searchLink !== null) { + searchLink.setAttribute("hidden", "true"); + searchButtonMobile.removeAttribute("hidden"); + buttons.push(searchButtonMobile); + } // Enhance desktop search - document - .querySelector(".navbar__search-form") - .setAttribute("hidden", "true"); + const searchForm = document + .querySelector(".navbar__search-form"); + if (searchForm !== null) { + searchForm.setAttribute("hidden", "true"); + } searchButton.removeAttribute("hidden"); // Open when the search button is clicked - [searchButton, searchButtonMobile].forEach((button) => + buttons.forEach((button) => button.addEventListener("click", show), ); @@ -390,7 +343,10 @@ const initSearchUI = ({ searchCallback, language, limit = 30 }) => { const icon = ["General", "Extension"].includes(item.type) ? DOCUMENT_ICON : BRACES_ICON; - const link = `/manual/${encodeURIComponent(language)}/${encodeURIComponent(item.id)}.php`; + let link = `/manual/${encodeURIComponent(language)}/${encodeURIComponent(item.id)}.php`; + if (language === 'local') { + link = encodeURIComponent(item.id) + '.html'; + } const description = item.type !== "General" @@ -459,7 +415,7 @@ const initSearchUI = ({ searchCallback, language, limit = 30 }) => { if (selectedIndex !== -1) { event.preventDefault(); resultsElements[selectedIndex].click(); - } else { + } else if (language !== 'local') { window.location.href = `/search.php?lang=${language}&q=${encodeURIComponent(inputElement.value)}`; } break;
