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;

Reply via email to