Author: AllenJB (AllenJB)
Committer: GitHub (web-flow)
Pusher: derickr
Date: 2025-11-04T17:17:06Z
Commit: Update search to use new pre-combined indexes + local docs support (#… · php/web-php@658fafe · GitHub
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;