diff --git a/README.md b/README.md index 43dcd59..08dcbd2 100644 --- a/README.md +++ b/README.md @@ -444,7 +444,7 @@ Common failure modes: - WebSocket upgrade fails Check that nginx is routing `.ws.uce` to `proxy_pass`, not `fastcgi_pass`, and that `HTTP_PORT` is reachable on localhost. - Requests compile but immediately crash - Check `journalctl -u uce.service` and clear stale generated artifacts under `BIN_DIRECTORY` if you have changed runtime ABI or entrypoint signatures. + Check `journalctl -u uce.service`. Generated units now carry an ABI metadata sidecar and should be recompiled automatically after runtime ABI changes, but clearing stale artifacts under `BIN_DIRECTORY` is still a useful last-resort recovery step if the cache has been damaged manually. - nginx serves raw source or internal files Tighten the server root and add explicit deny rules for non-public directories. diff --git a/scripts/setup.h.template b/scripts/setup.h.template index bf8e77d..4c0208c 100644 --- a/scripts/setup.h.template +++ b/scripts/setup.h.template @@ -3,7 +3,7 @@ /*load_declarations*/ -extern "C" void set_current_request(Request* _request) +extern "C" void __uce_set_current_request(Request* _request) { context = _request; /*load_units*/ diff --git a/site/doc/areas/string.txt b/site/doc/areas/string.txt index 509f44b..0cc2ffe 100644 --- a/site/doc/areas/string.txt +++ b/site/doc/areas/string.txt @@ -1,6 +1,7 @@ String Functions ascii_safe_name +contains concat filter first @@ -8,6 +9,10 @@ join json_encode nibble print +strpos +str_ends_with +str_starts_with +substr split split_space split_utf8 diff --git a/site/doc/areas/types.txt b/site/doc/areas/types.txt index c90239e..30a1964 100644 --- a/site/doc/areas/types.txt +++ b/site/doc/areas/types.txt @@ -1,5 +1,6 @@ Types +array_merge DTree get_by_path set_status diff --git a/site/doc/areas/uri.txt b/site/doc/areas/uri.txt index cb9154e..05d87a2 100644 --- a/site/doc/areas/uri.txt +++ b/site/doc/areas/uri.txt @@ -1,6 +1,7 @@ URI Functions encode_query +redirect session_id_create parse_query uri_decode diff --git a/site/doc/index.uce b/site/doc/index.uce index c7c9871..2d472eb 100644 --- a/site/doc/index.uce +++ b/site/doc/index.uce @@ -168,6 +168,10 @@ RENDER(Request& context) { ?>

Description

Related PHP and JS

Related

types + +:related +**PHP:** `$_SERVER`, `$_GET`, `$_POST`, `$_COOKIE`, `$_SESSION`, `header()`, and `http_response_code()` +**JavaScript / Node.js:** Express `req` and `res`, Fetch `Request`, `Headers`, cookies or session middleware, and per-connection state in WebSocket handlers diff --git a/site/doc/pages/1_COMPONENT.txt b/site/doc/pages/1_COMPONENT.txt index b447480..dcc284e 100644 --- a/site/doc/pages/1_COMPONENT.txt +++ b/site/doc/pages/1_COMPONENT.txt @@ -34,3 +34,7 @@ Examples: >component_render >1_RENDER >1_WS + +:related +**PHP:** View partials, reusable include files, small template helpers, and server-side component-like rendering patterns +**JavaScript / Node.js:** Reusable component functions, React or Vue components, and server-rendered partials diff --git a/site/doc/pages/1_RENDER.txt b/site/doc/pages/1_RENDER.txt index 3fcd182..94c1b8a 100644 --- a/site/doc/pages/1_RENDER.txt +++ b/site/doc/pages/1_RENDER.txt @@ -20,3 +20,7 @@ Pages intended to serve WebSocket traffic may expose both `RENDER(Request& conte :see >ob + +:related +**PHP:** Front controller entrypoints, template files, `include`, `require`, and route handlers that write the HTTP response +**JavaScript / Node.js:** Express or Fastify route handlers, page controller functions, and SSR entrypoints that build a response diff --git a/site/doc/pages/1_WS.txt b/site/doc/pages/1_WS.txt index ce39d39..409f2a3 100644 --- a/site/doc/pages/1_WS.txt +++ b/site/doc/pages/1_WS.txt @@ -12,15 +12,15 @@ UCE reassembles fragmented messages before calling `WS(Request& context)`. Text The current message data is available in `context.call`: -context.call["message"] : current message payload +- `context.call["message"]`: current message payload +- `context.call["connection_id"]`: sender connection ID +- `context.call["scope"]`: current endpoint scope +- `context.call["opcode"]`: WebSocket opcode of the current message +- `context.call["document_uri"]`: request URI of the current endpoint -context.call["connection_id"] : sender connection ID - -context.call["scope"] : current endpoint scope - -context.call["opcode"] : WebSocket opcode of the current message - -context.call["document_uri"] : request URI of the current endpoint +:related +**PHP:** Ratchet `onMessage`, Workerman WebSocket handlers, or lower-level callbacks around accepted socket connections. +**JavaScript / Node.js:** Browser `WebSocket` `message` handlers and Node `ws` server `connection` and `message` callbacks. :see >websocket diff --git a/site/doc/pages/1_preprocessor.txt b/site/doc/pages/1_preprocessor.txt index 9609554..4158b57 100644 --- a/site/doc/pages/1_preprocessor.txt +++ b/site/doc/pages/1_preprocessor.txt @@ -18,7 +18,7 @@ The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all :Pipeline - The generated file starts by including `COMPILER_SYS_PATH/src/lib/uce_lib.h`. -- It then inlines the configured setup template from `SETUP_TEMPLATE` (by default `scripts/setup.h.template`), which defines `set_current_request(Request*)`. +- It then inlines the configured setup template from `SETUP_TEMPLATE` (by default `scripts/setup.h.template`), which defines the internal hook `__uce_set_current_request(Request*)`. - It inserts `#line 1` before page code so compiler diagnostics point back to the original `.uce` file. - Each literal block is rewritten into one or more `print(R"( ... )");` calls. - `` temporarily breaks out of literal printing, emits the enclosed C++ unchanged, then resumes literal output. @@ -26,7 +26,8 @@ The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all - `` becomes `print(...);` and is intended for trusted markup or already-escaped content. - `#load "file.uce"` is replaced with a generated C++ `#include` that points at the loaded unit's preprocessed `.cpp` file under `BIN_DIRECTORY`. - Lines beginning with `EXPORT` are scanned so their declarations can be written to a sibling `.exports.txt` file. -- Lines beginning with `COMPONENT:NAME(...)` are rewritten into exported `component_render_NAME(...)` functions for the component helpers. +- Lines beginning with `RENDER:NAME(...)` are rewritten into exported `__uce_render_NAME(...)` functions. +- Lines beginning with `COMPONENT:NAME(...)` are rewritten into exported `__uce_component_NAME(...)` functions for the component helpers. - The final generated source is written to `BIN_DIRECTORY + src_path + "/" + source_file + ".cpp"`. - `scripts/compile` then compiles that generated `.cpp` into `source_file + ".so"` with `clang++ -shared -std=c++20 ...`. @@ -96,3 +97,7 @@ unit_render unit_call 0_context 1_COMPONENT + +:related +**PHP:** Template tags like ``, ``, output buffering, and compile-time include patterns +**JavaScript / Node.js:** JSX transforms, tagged templates, server-side rendering pipelines, and build-time HTML generation diff --git a/site/doc/pages/DTree.txt b/site/doc/pages/DTree.txt index 10787b8..6cdd37a 100644 --- a/site/doc/pages/DTree.txt +++ b/site/doc/pages/DTree.txt @@ -9,20 +9,24 @@ Dynamic tree/container type used throughout UCE for structured data. Use `t["key"]` to access or create child entries. Use `push()` / `pop()` when treating it like an array-like container with numeric string keys. Common uses include: -`json_decode()` / `json_encode()` -`context.var` -`context.call` -`unit_call()` return values +- `json_decode()` / `json_encode()` +- `context.var` +- `context.call` +- `unit_call()` return values Useful methods include: -`to_string()` -`to_json()` -`get_type_name()` -`get_by_path()` -`set_bool()` -`remove()` -`clear()` -`each()` +- `to_string()` +- `to_json()` +- `get_type_name()` +- `get_by_path()` +- `set_bool()` +- `remove()` +- `clear()` +- `each()` + +:related +**PHP:** Nested associative arrays, `stdClass`, decoded JSON trees, and helper accessors for deep array paths. +**JavaScript / Node.js:** Plain objects, arrays, `Map`, and JSON-shaped data passed between handlers. :see >types diff --git a/site/doc/pages/String.txt b/site/doc/pages/String.txt index 73987a7..dd56fb8 100644 --- a/site/doc/pages/String.txt +++ b/site/doc/pages/String.txt @@ -14,3 +14,7 @@ For UTF-8-aware splitting, use helpers such as `split_utf8()` instead of assumin :see >types + +:related +**PHP:** Native PHP strings with helpers such as `substr()`, `trim()`, `explode()`, and `implode()` +**JavaScript / Node.js:** JavaScript `string` values with methods such as `slice()`, `trim()`, `split()`, and `join()` diff --git a/site/doc/pages/StringMap.txt b/site/doc/pages/StringMap.txt index ed72705..fc6dd0b 100644 --- a/site/doc/pages/StringMap.txt +++ b/site/doc/pages/StringMap.txt @@ -20,3 +20,7 @@ Related helpers such as `parse_query()` and `encode_query()` convert between que :see >types + +:related +**PHP:** Associative arrays keyed by string, especially request bags like `$_GET` and `$_SERVER` +**JavaScript / Node.js:** Plain objects, dictionaries, `Map`, and header or query objects in web frameworks diff --git a/site/doc/pages/ascii_safe_name.txt b/site/doc/pages/ascii_safe_name.txt index 85bfb97..fe10e89 100644 --- a/site/doc/pages/ascii_safe_name.txt +++ b/site/doc/pages/ascii_safe_name.txt @@ -12,3 +12,7 @@ This is useful when turning user- or config-provided names into handler suffixes :see >string + +:related +**PHP:** Slug or safe-filename helpers built with `preg_replace()` and transliteration utilities +**JavaScript / Node.js:** Regex-based slugify or safe-name helpers used for URLs and filenames diff --git a/site/doc/pages/basename.txt b/site/doc/pages/basename.txt index 9b14648..b7c6fa6 100644 --- a/site/doc/pages/basename.txt +++ b/site/doc/pages/basename.txt @@ -10,3 +10,7 @@ Isolates the file name component from a path/file name. :see >sys + +:related +**PHP:** `basename()` +**JavaScript / Node.js:** Node `path.basename()` diff --git a/site/doc/pages/component.txt b/site/doc/pages/component.txt index e95fa75..b7bab63 100644 --- a/site/doc/pages/component.txt +++ b/site/doc/pages/component.txt @@ -28,3 +28,7 @@ Example: :see >ob + +:related +**PHP:** Reusable template partials or helper-rendered view fragments returned as strings +**JavaScript / Node.js:** Component render helpers, especially patterns that return markup as a string diff --git a/site/doc/pages/component_exists.txt b/site/doc/pages/component_exists.txt index 677c34b..7cc6429 100644 --- a/site/doc/pages/component_exists.txt +++ b/site/doc/pages/component_exists.txt @@ -12,3 +12,7 @@ This is useful when a page wants to render an optional component if it is presen :see >ob + +:related +**PHP:** `function_exists()`, `class_exists()`, or file-existence checks before including a partial +**JavaScript / Node.js:** Module existence checks, dynamic import guards, or registry lookups for named components diff --git a/site/doc/pages/component_render.txt b/site/doc/pages/component_render.txt index fa1a7f4..1f0930b 100644 --- a/site/doc/pages/component_render.txt +++ b/site/doc/pages/component_render.txt @@ -19,3 +19,7 @@ Example: :see >ob + +:related +**PHP:** Rendering a partial directly into the current output buffer rather than returning a string +**JavaScript / Node.js:** Direct `res.write()`-style partial rendering or imperative component mount helpers diff --git a/site/doc/pages/component_resolve.txt b/site/doc/pages/component_resolve.txt index f8294fa..bc3bc71 100644 --- a/site/doc/pages/component_resolve.txt +++ b/site/doc/pages/component_resolve.txt @@ -12,3 +12,7 @@ This is primarily a debugging helper so you can see which concrete file a shorth :see >ob + +:related +**PHP:** Resolving include paths or view names to a concrete template file before rendering +**JavaScript / Node.js:** Resolving module paths, alias-based imports, or component registry entries diff --git a/site/doc/pages/concat.txt b/site/doc/pages/concat.txt index 30c7328..7fba11a 100644 --- a/site/doc/pages/concat.txt +++ b/site/doc/pages/concat.txt @@ -9,3 +9,7 @@ Returns a string with all the parameters concatenated into one. :see >string + +:related +**PHP:** String concatenation with `.` or helpers like `implode()` +**JavaScript / Node.js:** String concatenation with `+`, template literals, or `Array.prototype.join()` diff --git a/site/doc/pages/cwd_get.txt b/site/doc/pages/cwd_get.txt index 94508e2..4ee8fe4 100644 --- a/site/doc/pages/cwd_get.txt +++ b/site/doc/pages/cwd_get.txt @@ -9,3 +9,7 @@ Returns the current working directory. :see >sys + +:related +**PHP:** `getcwd()` +**JavaScript / Node.js:** Node `process.cwd()` diff --git a/site/doc/pages/cwd_set.txt b/site/doc/pages/cwd_set.txt index c4c856d..a36f7cc 100644 --- a/site/doc/pages/cwd_set.txt +++ b/site/doc/pages/cwd_set.txt @@ -9,3 +9,7 @@ Sets a new working directory. :see >sys + +:related +**PHP:** `chdir()` +**JavaScript / Node.js:** Node `process.chdir()` diff --git a/site/doc/pages/dirname.txt b/site/doc/pages/dirname.txt index 4c21233..f171668 100644 --- a/site/doc/pages/dirname.txt +++ b/site/doc/pages/dirname.txt @@ -10,3 +10,7 @@ Isolates the directory name component from a path/file name. :see >sys + +:related +**PHP:** `dirname()` +**JavaScript / Node.js:** Node `path.dirname()` diff --git a/site/doc/pages/draw_float.txt b/site/doc/pages/draw_float.txt index 036a8f9..64fb453 100644 --- a/site/doc/pages/draw_float.txt +++ b/site/doc/pages/draw_float.txt @@ -11,3 +11,7 @@ This function works exactly like generate_float(), but context.random_index is u :see >noise + +:related +**PHP:** `random_int()`, `mt_rand()`, and custom numeric random helpers +**JavaScript / Node.js:** `Math.random()` or `crypto.getRandomValues()`-based helpers for numeric ranges diff --git a/site/doc/pages/draw_int.txt b/site/doc/pages/draw_int.txt index 86dcea1..412b476 100644 --- a/site/doc/pages/draw_int.txt +++ b/site/doc/pages/draw_int.txt @@ -11,3 +11,7 @@ This function works exactly like generate_int(), but context.random_index is use :see >noise + +:related +**PHP:** `random_int()`, `mt_rand()`, and custom numeric random helpers +**JavaScript / Node.js:** `Math.random()` or `crypto.getRandomValues()`-based helpers for numeric ranges diff --git a/site/doc/pages/encode_query.txt b/site/doc/pages/encode_query.txt index 67acf25..921d012 100644 --- a/site/doc/pages/encode_query.txt +++ b/site/doc/pages/encode_query.txt @@ -10,3 +10,7 @@ Encodes a StringMap containing URL parameters into a single String. :see >uri + +:related +**PHP:** `http_build_query()` +**JavaScript / Node.js:** `URLSearchParams` and `toString()` diff --git a/site/doc/pages/expand_path.txt b/site/doc/pages/expand_path.txt index b77e7c3..a1b1dd2 100644 --- a/site/doc/pages/expand_path.txt +++ b/site/doc/pages/expand_path.txt @@ -11,3 +11,7 @@ Converts a relative path name into an absolute path, using the current working d :see >sys + +:related +**PHP:** `realpath()` and application-specific path normalization helpers +**JavaScript / Node.js:** Node `path.resolve()` and `path.normalize()` diff --git a/site/doc/pages/file_append.txt b/site/doc/pages/file_append.txt index 82fce57..f1b9759 100644 --- a/site/doc/pages/file_append.txt +++ b/site/doc/pages/file_append.txt @@ -10,3 +10,7 @@ Opens or creates a given file and appends data to it. :see >sys + +:related +**PHP:** `file_put_contents($file, $data, FILE_APPEND)` +**JavaScript / Node.js:** Node `fs.appendFileSync()` or `fs.promises.appendFile()` diff --git a/site/doc/pages/file_exists.txt b/site/doc/pages/file_exists.txt index 5b09447..2aad107 100644 --- a/site/doc/pages/file_exists.txt +++ b/site/doc/pages/file_exists.txt @@ -10,3 +10,7 @@ Checks whether the file or path specified by 'path' exists. :see >sys + +:related +**PHP:** `file_exists()` or `is_file()` +**JavaScript / Node.js:** Node `fs.existsSync()` or `fs.stat()` diff --git a/site/doc/pages/file_get_contents.txt b/site/doc/pages/file_get_contents.txt index 2ff26e4..b221b7f 100644 --- a/site/doc/pages/file_get_contents.txt +++ b/site/doc/pages/file_get_contents.txt @@ -10,3 +10,7 @@ Reads the file identified by 'file_name' and returns it as a String. If the file :see >sys + +:related +**PHP:** `file_get_contents()` +**JavaScript / Node.js:** Node `fs.readFileSync()` or `fs.promises.readFile()` diff --git a/site/doc/pages/file_mtime.txt b/site/doc/pages/file_mtime.txt index be5bddb..1e3df7d 100644 --- a/site/doc/pages/file_mtime.txt +++ b/site/doc/pages/file_mtime.txt @@ -11,3 +11,7 @@ Retrieves the last modification date of 'file_name' as a Unix timestamp. :see >sys >time + +:related +**PHP:** `filemtime()` +**JavaScript / Node.js:** Node `fs.statSync().mtimeMs` or `fs.promises.stat()` diff --git a/site/doc/pages/file_put_contents.txt b/site/doc/pages/file_put_contents.txt index fdecc37..f6e9d55 100644 --- a/site/doc/pages/file_put_contents.txt +++ b/site/doc/pages/file_put_contents.txt @@ -11,3 +11,7 @@ Writes the String 'content' into a file identified by 'file_name'. Any pre-exist :see >sys + +:related +**PHP:** `file_put_contents()` +**JavaScript / Node.js:** Node `fs.writeFileSync()` or `fs.promises.writeFile()` diff --git a/site/doc/pages/file_unlink.txt b/site/doc/pages/file_unlink.txt index 446485a..10c1dbf 100644 --- a/site/doc/pages/file_unlink.txt +++ b/site/doc/pages/file_unlink.txt @@ -9,3 +9,7 @@ Deletes the file identified by `file_name`. :see >sys + +:related +**PHP:** `unlink()` +**JavaScript / Node.js:** Node `fs.unlinkSync()` or `fs.promises.unlink()` diff --git a/site/doc/pages/filter.txt b/site/doc/pages/filter.txt index db12c41..4113f7e 100644 --- a/site/doc/pages/filter.txt +++ b/site/doc/pages/filter.txt @@ -12,3 +12,7 @@ Returns a list containing the members of 'items' for which 'f' returned boolean :see >string + +:related +**PHP:** `array_filter()` for arrays or custom predicate-based filtering over strings and collections +**JavaScript / Node.js:** `Array.prototype.filter()` and custom predicate helpers diff --git a/site/doc/pages/first.txt b/site/doc/pages/first.txt index 98457f1..9126e66 100644 --- a/site/doc/pages/first.txt +++ b/site/doc/pages/first.txt @@ -10,3 +10,7 @@ Given a variable number of String parameters, the first() function returns the f :see >string + +:related +**PHP:** Null-coalescing patterns like `$a ?? $b`, fallback helpers, or `reset()` for arrays +**JavaScript / Node.js:** `??`, `||`, and small fallback helper functions diff --git a/site/doc/pages/float_val.txt b/site/doc/pages/float_val.txt index 395cfd9..b2d76c0 100644 --- a/site/doc/pages/float_val.txt +++ b/site/doc/pages/float_val.txt @@ -8,3 +8,7 @@ return value : a f64 containing the number (0 if no number could be identified). :desc Extracts a floating point number from a String. + +:related +**PHP:** `floatval()` +**JavaScript / Node.js:** `Number()` or `parseFloat()` diff --git a/site/doc/pages/gen_float.txt b/site/doc/pages/gen_float.txt index 1f23c67..42ea599 100644 --- a/site/doc/pages/gen_float.txt +++ b/site/doc/pages/gen_float.txt @@ -14,3 +14,7 @@ Generates a noise value between 'from' and 'to', given the 'index' and 'seed' nu :see >noise + +:related +**PHP:** `random_int()`, `mt_rand()`, and custom numeric random helpers +**JavaScript / Node.js:** `Math.random()` or `crypto.getRandomValues()`-based helpers for numeric ranges diff --git a/site/doc/pages/gen_int.txt b/site/doc/pages/gen_int.txt index 2152938..1a0aa54 100644 --- a/site/doc/pages/gen_int.txt +++ b/site/doc/pages/gen_int.txt @@ -14,3 +14,7 @@ Generates a noise value between 'from' and 'to', given the 'index' and 'seed' nu :see >noise + +:related +**PHP:** `random_int()`, `mt_rand()`, and custom numeric random helpers +**JavaScript / Node.js:** `Math.random()` or `crypto.getRandomValues()`-based helpers for numeric ranges diff --git a/site/doc/pages/gen_noise01.txt b/site/doc/pages/gen_noise01.txt index dd8aff8..a828fc1 100644 --- a/site/doc/pages/gen_noise01.txt +++ b/site/doc/pages/gen_noise01.txt @@ -11,3 +11,7 @@ Generates a noise value in the range from 0 to 1 for the given 'index' and 'seed :see >noise + +:related +**PHP:** There is no built-in PHP equivalent; this is closest to userland noise or deterministic pseudo-random helpers +**JavaScript / Node.js:** There is no built-in browser equivalent; closest matches are userland noise libraries or deterministic PRNG helpers diff --git a/site/doc/pages/gen_noise32.txt b/site/doc/pages/gen_noise32.txt index 8fe7a2a..021ea5a 100644 --- a/site/doc/pages/gen_noise32.txt +++ b/site/doc/pages/gen_noise32.txt @@ -11,3 +11,7 @@ Generates a noise value for the given 'index' and 'seed' values. :see >noise + +:related +**PHP:** There is no built-in PHP equivalent; this is closest to userland noise or deterministic pseudo-random helpers +**JavaScript / Node.js:** There is no built-in browser equivalent; closest matches are userland noise libraries or deterministic PRNG helpers diff --git a/site/doc/pages/gen_noise64.txt b/site/doc/pages/gen_noise64.txt index 9e7f0d1..c8c1757 100644 --- a/site/doc/pages/gen_noise64.txt +++ b/site/doc/pages/gen_noise64.txt @@ -11,3 +11,7 @@ Generates a noise value for the given 'index' and 'seed' values. :see >noise + +:related +**PHP:** There is no built-in PHP equivalent; this is closest to userland noise or deterministic pseudo-random helpers +**JavaScript / Node.js:** There is no built-in browser equivalent; closest matches are userland noise libraries or deterministic PRNG helpers diff --git a/site/doc/pages/gen_sha1.txt b/site/doc/pages/gen_sha1.txt index d992497..0fb414c 100644 --- a/site/doc/pages/gen_sha1.txt +++ b/site/doc/pages/gen_sha1.txt @@ -11,3 +11,7 @@ Returns the sha1 hash of 's'. :see >noise + +:related +**PHP:** `sha1()` +**JavaScript / Node.js:** Node `crypto.createHash("sha1")` or browser `SubtleCrypto` wrappers diff --git a/site/doc/pages/get_by_path.txt b/site/doc/pages/get_by_path.txt index 89a91f1..ceb941a 100644 --- a/site/doc/pages/get_by_path.txt +++ b/site/doc/pages/get_by_path.txt @@ -18,3 +18,7 @@ Typical usage: DTree 0_context >types + +:related +**PHP:** Deep array access helpers for associative arrays, decoded JSON, or configuration trees +**JavaScript / Node.js:** Optional chaining like `obj?.a?.b`, lodash `get`, or small path-walking helpers diff --git a/site/doc/pages/html_escape.txt b/site/doc/pages/html_escape.txt index 802d86a..c6810a6 100644 --- a/site/doc/pages/html_escape.txt +++ b/site/doc/pages/html_escape.txt @@ -11,3 +11,7 @@ Returns a version of the input string where the following characters have been r - < → lt; - > → > - " → " + +:related +**PHP:** `htmlspecialchars()` or `htmlentities()` +**JavaScript / Node.js:** Safe text insertion via `textContent` or server-side HTML escaping libraries diff --git a/site/doc/pages/int_val.txt b/site/doc/pages/int_val.txt index 110cc3a..6c8e59c 100644 --- a/site/doc/pages/int_val.txt +++ b/site/doc/pages/int_val.txt @@ -9,3 +9,7 @@ return value : a u64 containing the number (0 if no number could be identified). :desc Extracts an integer from a String. + +:related +**PHP:** `intval()` +**JavaScript / Node.js:** `Number()` or `parseInt()` diff --git a/site/doc/pages/join.txt b/site/doc/pages/join.txt index d622e92..5efd0b5 100644 --- a/site/doc/pages/join.txt +++ b/site/doc/pages/join.txt @@ -11,3 +11,7 @@ Joins the items contained in 'l' into a single String. :see >string + +:related +**PHP:** `implode()` +**JavaScript / Node.js:** `Array.prototype.join()` diff --git a/site/doc/pages/json_decode.txt b/site/doc/pages/json_decode.txt index 5b5f86f..4535b41 100644 --- a/site/doc/pages/json_decode.txt +++ b/site/doc/pages/json_decode.txt @@ -8,3 +8,7 @@ return value : a DTree object containing the deserialized JSON data :desc Deserializes 's' into a DTree structure. + +:related +**PHP:** `json_decode()` +**JavaScript / Node.js:** `JSON.parse()` diff --git a/site/doc/pages/json_encode.txt b/site/doc/pages/json_encode.txt index 7d6f2c9..af2a7df 100644 --- a/site/doc/pages/json_encode.txt +++ b/site/doc/pages/json_encode.txt @@ -13,3 +13,7 @@ Serializes either a `String` or a `DTree` into JSON notation. When passed a `String`, `json_encode()` returns a quoted and escaped JSON string literal. When passed a `DTree`, scalar values are serialized directly and nested map values are emitted as JSON objects. + +:related +**PHP:** `json_encode()` +**JavaScript / Node.js:** `JSON.stringify()` diff --git a/site/doc/pages/load.txt b/site/doc/pages/load.txt index e290fb3..1868afe 100644 --- a/site/doc/pages/load.txt +++ b/site/doc/pages/load.txt @@ -9,3 +9,7 @@ Includes another UCE file :see >ob + +:related +**PHP:** `include`, `require`, and config-loader patterns that resolve and import another file +**JavaScript / Node.js:** `import()`, `require()`, or file-loader helpers that resolve modules at runtime diff --git a/site/doc/pages/ls.txt b/site/doc/pages/ls.txt index f1e0511..cc8fc7e 100644 --- a/site/doc/pages/ls.txt +++ b/site/doc/pages/ls.txt @@ -10,3 +10,7 @@ Returns a list of files and subdirectories within the given 'path'. :see >sys + +:related +**PHP:** `scandir()`, `glob()`, or `DirectoryIterator` +**JavaScript / Node.js:** Node `fs.readdirSync()` or `fs.promises.readdir()` diff --git a/site/doc/pages/markdown_to_ast.txt b/site/doc/pages/markdown_to_ast.txt index b67e27b..58f7842 100644 --- a/site/doc/pages/markdown_to_ast.txt +++ b/site/doc/pages/markdown_to_ast.txt @@ -75,3 +75,7 @@ component component_render json_encode DTree + +:related +**PHP:** CommonMark or Parsedown parsing pipelines that expose a syntax tree or token stream +**JavaScript / Node.js:** `remark`, `micromark`, or `markdown-it` token streams and AST-like structures diff --git a/site/doc/pages/markdown_to_html.txt b/site/doc/pages/markdown_to_html.txt index 04a153f..259bf21 100644 --- a/site/doc/pages/markdown_to_html.txt +++ b/site/doc/pages/markdown_to_html.txt @@ -98,3 +98,7 @@ component component_render json_decode String + +:related +**PHP:** Parsedown, League CommonMark, or similar Markdown-to-HTML renderers +**JavaScript / Node.js:** `marked`, `markdown-it`, `remark-html`, or other Markdown renderers diff --git a/site/doc/pages/memcache_command.txt b/site/doc/pages/memcache_command.txt index 700e8c5..bb1f6ff 100644 --- a/site/doc/pages/memcache_command.txt +++ b/site/doc/pages/memcache_command.txt @@ -11,3 +11,7 @@ Executes a command on an open memcache connection. :see >memcache + +:related +**PHP:** The `Memcache` or `Memcached` extension methods such as `get()`, `set()`, `delete()`, and `getMulti()` +**JavaScript / Node.js:** Node clients like `memjs`, `memcached`, or similar Memcached packages diff --git a/site/doc/pages/memcache_connect.txt b/site/doc/pages/memcache_connect.txt index ebbe17f..d1c9380 100644 --- a/site/doc/pages/memcache_connect.txt +++ b/site/doc/pages/memcache_connect.txt @@ -11,3 +11,7 @@ Connects to a memcache server instance. :see >memcache + +:related +**PHP:** The `Memcache` or `Memcached` extension methods such as `get()`, `set()`, `delete()`, and `getMulti()` +**JavaScript / Node.js:** Node clients like `memjs`, `memcached`, or similar Memcached packages diff --git a/site/doc/pages/memcache_delete.txt b/site/doc/pages/memcache_delete.txt index e0bd946..9780fe2 100644 --- a/site/doc/pages/memcache_delete.txt +++ b/site/doc/pages/memcache_delete.txt @@ -11,3 +11,7 @@ Deletes entry specified by the 'key'. :see >memcache + +:related +**PHP:** The `Memcache` or `Memcached` extension methods such as `get()`, `set()`, `delete()`, and `getMulti()` +**JavaScript / Node.js:** Node clients like `memjs`, `memcached`, or similar Memcached packages diff --git a/site/doc/pages/memcache_get.txt b/site/doc/pages/memcache_get.txt index c7f2c9b..646db7e 100644 --- a/site/doc/pages/memcache_get.txt +++ b/site/doc/pages/memcache_get.txt @@ -12,3 +12,7 @@ Retrieves a value from an existing connection to a Memcache server. :see >memcache + +:related +**PHP:** The `Memcache` or `Memcached` extension methods such as `get()`, `set()`, `delete()`, and `getMulti()` +**JavaScript / Node.js:** Node clients like `memjs`, `memcached`, or similar Memcached packages diff --git a/site/doc/pages/memcache_get_multiple.txt b/site/doc/pages/memcache_get_multiple.txt index cdf1a02..bc9f56d 100644 --- a/site/doc/pages/memcache_get_multiple.txt +++ b/site/doc/pages/memcache_get_multiple.txt @@ -11,3 +11,7 @@ Retrieves a bunch of entries all at once. :see >memcache + +:related +**PHP:** The `Memcache` or `Memcached` extension methods such as `get()`, `set()`, `delete()`, and `getMulti()` +**JavaScript / Node.js:** Node clients like `memjs`, `memcached`, or similar Memcached packages diff --git a/site/doc/pages/memcache_set.txt b/site/doc/pages/memcache_set.txt index a67457c..4755a1f 100644 --- a/site/doc/pages/memcache_set.txt +++ b/site/doc/pages/memcache_set.txt @@ -13,3 +13,7 @@ Stores a 'value' on the Memcache server. :see >memcache + +:related +**PHP:** The `Memcache` or `Memcached` extension methods such as `get()`, `set()`, `delete()`, and `getMulti()` +**JavaScript / Node.js:** Node clients like `memjs`, `memcached`, or similar Memcached packages diff --git a/site/doc/pages/mkdir.txt b/site/doc/pages/mkdir.txt index e9da8fa..5c02f38 100644 --- a/site/doc/pages/mkdir.txt +++ b/site/doc/pages/mkdir.txt @@ -10,3 +10,7 @@ Creates a directory stated by 'path' :see >sys + +:related +**PHP:** `mkdir()` +**JavaScript / Node.js:** Node `fs.mkdirSync()` or `fs.promises.mkdir()` diff --git a/site/doc/pages/mysql_connect.txt b/site/doc/pages/mysql_connect.txt index d5811cc..d4f9c9a 100644 --- a/site/doc/pages/mysql_connect.txt +++ b/site/doc/pages/mysql_connect.txt @@ -12,3 +12,7 @@ Establishes a connection to a MySQL server. :see >mysql + +:related +**PHP:** Modern PHP database access through `mysqli_*` or PDO methods rather than the old `mysql_*` family +**JavaScript / Node.js:** Node database clients such as `mysql2`, query builders, or ORM adapters diff --git a/site/doc/pages/mysql_disconnect.txt b/site/doc/pages/mysql_disconnect.txt index 850c7b7..c889821 100644 --- a/site/doc/pages/mysql_disconnect.txt +++ b/site/doc/pages/mysql_disconnect.txt @@ -9,3 +9,7 @@ Closes a connection to a MySQL server. :see >mysql + +:related +**PHP:** Modern PHP database access through `mysqli_*` or PDO methods rather than the old `mysql_*` family +**JavaScript / Node.js:** Node database clients such as `mysql2`, query builders, or ORM adapters diff --git a/site/doc/pages/mysql_error.txt b/site/doc/pages/mysql_error.txt index 79ab117..b8f6ee9 100644 --- a/site/doc/pages/mysql_error.txt +++ b/site/doc/pages/mysql_error.txt @@ -10,3 +10,7 @@ Returns the last error message from a connection to a MySQL server. :see >mysql + +:related +**PHP:** Modern PHP database access through `mysqli_*` or PDO methods rather than the old `mysql_*` family +**JavaScript / Node.js:** Node database clients such as `mysql2`, query builders, or ORM adapters diff --git a/site/doc/pages/mysql_escape.txt b/site/doc/pages/mysql_escape.txt index dccb730..a0a030b 100644 --- a/site/doc/pages/mysql_escape.txt +++ b/site/doc/pages/mysql_escape.txt @@ -11,3 +11,7 @@ Escapes a string such that it can be passed as a safe value into an SQL expressi :see >mysql + +:related +**PHP:** Modern PHP database access through `mysqli_*` or PDO methods rather than the old `mysql_*` family +**JavaScript / Node.js:** Node database clients such as `mysql2`, query builders, or ORM adapters diff --git a/site/doc/pages/mysql_insert_id.txt b/site/doc/pages/mysql_insert_id.txt index b0b331c..b44fa99 100644 --- a/site/doc/pages/mysql_insert_id.txt +++ b/site/doc/pages/mysql_insert_id.txt @@ -10,3 +10,7 @@ This retrieves the last row ID that was used for a column with an AUTO_INCREMENT :see >mysql + +:related +**PHP:** Modern PHP database access through `mysqli_*` or PDO methods rather than the old `mysql_*` family +**JavaScript / Node.js:** Node database clients such as `mysql2`, query builders, or ORM adapters diff --git a/site/doc/pages/mysql_query.txt b/site/doc/pages/mysql_query.txt index 89d2abb..daf6d99 100644 --- a/site/doc/pages/mysql_query.txt +++ b/site/doc/pages/mysql_query.txt @@ -15,3 +15,7 @@ Executes a MySQL query and returns the resulting data (if any). :see >mysql + +:related +**PHP:** Modern PHP database access through `mysqli_*` or PDO methods rather than the old `mysql_*` family +**JavaScript / Node.js:** Node database clients such as `mysql2`, query builders, or ORM adapters diff --git a/site/doc/pages/nibble.txt b/site/doc/pages/nibble.txt index fd44f8c..8bd0322 100644 --- a/site/doc/pages/nibble.txt +++ b/site/doc/pages/nibble.txt @@ -11,3 +11,7 @@ Returns the part of 'haystack' before the first occurrence of 'delim', removing :see >string + +:related +**PHP:** Small parsing helpers built with `strpos()`, `substr()`, and `explode()` +**JavaScript / Node.js:** Manual parsing with `indexOf()`, `slice()`, and `split()` diff --git a/site/doc/pages/ob_close.txt b/site/doc/pages/ob_close.txt index b2fca80..40bb020 100644 --- a/site/doc/pages/ob_close.txt +++ b/site/doc/pages/ob_close.txt @@ -9,3 +9,7 @@ Discard the current output buffer. If are more output buffers on the stack, swit :see >ob + +:related +**PHP:** `ob_start()`, `ob_get_contents()`, `ob_get_clean()`, and related output-buffering APIs +**JavaScript / Node.js:** String accumulation, buffer capture, or render-to-string patterns in server code diff --git a/site/doc/pages/ob_get.txt b/site/doc/pages/ob_get.txt index 20492ea..382571c 100644 --- a/site/doc/pages/ob_get.txt +++ b/site/doc/pages/ob_get.txt @@ -9,3 +9,7 @@ Returns the contents of the current output buffer. :see >ob + +:related +**PHP:** `ob_start()`, `ob_get_contents()`, `ob_get_clean()`, and related output-buffering APIs +**JavaScript / Node.js:** String accumulation, buffer capture, or render-to-string patterns in server code diff --git a/site/doc/pages/ob_get_close.txt b/site/doc/pages/ob_get_close.txt index 9667f3d..ff45f89 100644 --- a/site/doc/pages/ob_get_close.txt +++ b/site/doc/pages/ob_get_close.txt @@ -10,3 +10,7 @@ If are more output buffers on the stack, switch to the next one. :see >ob + +:related +**PHP:** `ob_start()`, `ob_get_contents()`, `ob_get_clean()`, and related output-buffering APIs +**JavaScript / Node.js:** String accumulation, buffer capture, or render-to-string patterns in server code diff --git a/site/doc/pages/ob_start.txt b/site/doc/pages/ob_start.txt index 6e5138d..dbf0a99 100644 --- a/site/doc/pages/ob_start.txt +++ b/site/doc/pages/ob_start.txt @@ -9,3 +9,7 @@ Starts a new output buffer. All subsequent output will be directed into this buf :see >ob + +:related +**PHP:** `ob_start()`, `ob_get_contents()`, `ob_get_clean()`, and related output-buffering APIs +**JavaScript / Node.js:** String accumulation, buffer capture, or render-to-string patterns in server code diff --git a/site/doc/pages/parse_query.txt b/site/doc/pages/parse_query.txt index e3a569e..faf8468 100644 --- a/site/doc/pages/parse_query.txt +++ b/site/doc/pages/parse_query.txt @@ -10,3 +10,7 @@ Decodes a string of the format 'a=b&c=d' into a StringMap containing keyed entri :see >uri + +:related +**PHP:** `parse_str()` +**JavaScript / Node.js:** `URLSearchParams` or Node `querystring.parse()` diff --git a/site/doc/pages/path_join.txt b/site/doc/pages/path_join.txt index 8a1a59a..cc6322d 100644 --- a/site/doc/pages/path_join.txt +++ b/site/doc/pages/path_join.txt @@ -13,3 +13,7 @@ If `child` is empty, `base` is returned. If `child` already starts with `/`, it :see >sys + +:related +**PHP:** Manual path composition with `DIRECTORY_SEPARATOR` or helper wrappers around it +**JavaScript / Node.js:** Node `path.join()` diff --git a/site/doc/pages/print.txt b/site/doc/pages/print.txt index ae3d19d..272df0f 100644 --- a/site/doc/pages/print.txt +++ b/site/doc/pages/print.txt @@ -9,3 +9,7 @@ Appends data to the current request's output stream. :see >string + +:related +**PHP:** `echo`, `print`, and output buffering patterns +**JavaScript / Node.js:** `res.write()`, stream writes, or string-returning render helpers depending on context diff --git a/site/doc/pages/replace.txt b/site/doc/pages/replace.txt index 68a6e80..6b27a54 100644 --- a/site/doc/pages/replace.txt +++ b/site/doc/pages/replace.txt @@ -12,3 +12,7 @@ Replace all occurrences of 'search' with the string defined in 'replace_with'. :see >string + +:related +**PHP:** `str_replace()` or `preg_replace()` +**JavaScript / Node.js:** `String.prototype.replace()` or `replaceAll()` diff --git a/site/doc/pages/session_destroy.txt b/site/doc/pages/session_destroy.txt index 386cb26..3adfc98 100644 --- a/site/doc/pages/session_destroy.txt +++ b/site/doc/pages/session_destroy.txt @@ -9,3 +9,7 @@ Deletes the cookie specified by 'session_name' and clears the data stored under :see >session + +:related +**PHP:** `session_destroy()` +**JavaScript / Node.js:** Express session store teardown or manual cookie-session invalidation diff --git a/site/doc/pages/session_id_create.txt b/site/doc/pages/session_id_create.txt index fbb6616..c50edd0 100644 --- a/site/doc/pages/session_id_create.txt +++ b/site/doc/pages/session_id_create.txt @@ -9,3 +9,7 @@ Creates a session ID. :see >session + +:related +**PHP:** `session_create_id()` or custom session token generators +**JavaScript / Node.js:** `crypto.randomUUID()` or custom secure session ID generation diff --git a/site/doc/pages/session_start.txt b/site/doc/pages/session_start.txt index c360f40..1130600 100644 --- a/site/doc/pages/session_start.txt +++ b/site/doc/pages/session_start.txt @@ -16,3 +16,7 @@ context.session : the current session data. The session data is automatically sa :see >session + +:related +**PHP:** `session_start()` +**JavaScript / Node.js:** Express `express-session`, `cookie-session`, or similar request-bound session middleware diff --git a/site/doc/pages/set_status.txt b/site/doc/pages/set_status.txt index 9e6067f..2adabe1 100644 --- a/site/doc/pages/set_status.txt +++ b/site/doc/pages/set_status.txt @@ -13,3 +13,7 @@ When `reason` is omitted, UCE fills in a standard reason phrase for common HTTP :see 0_context >types + +:related +**PHP:** `http_response_code()` and explicit status-line header control +**JavaScript / Node.js:** `res.status(...)`, `Response` init status, or low-level status assignment on an HTTP response diff --git a/site/doc/pages/shell_escape.txt b/site/doc/pages/shell_escape.txt index f55f1a6..15776a5 100644 --- a/site/doc/pages/shell_escape.txt +++ b/site/doc/pages/shell_escape.txt @@ -10,3 +10,7 @@ Escapes a parameter for shell_exec :see >sys + +:related +**PHP:** `escapeshellarg()` and `escapeshellcmd()` +**JavaScript / Node.js:** There is no direct built-in equivalent; prefer `spawn()` argument arrays and avoid shell interpolation diff --git a/site/doc/pages/shell_exec.txt b/site/doc/pages/shell_exec.txt index 8c2cdf1..acea46a 100644 --- a/site/doc/pages/shell_exec.txt +++ b/site/doc/pages/shell_exec.txt @@ -10,3 +10,7 @@ Executes a Linux shell command and returns the generated output :see >sys + +:related +**PHP:** `shell_exec()`, `exec()`, or `proc_open()` +**JavaScript / Node.js:** Node `child_process.exec()` or `spawn()` diff --git a/site/doc/pages/socket_close.txt b/site/doc/pages/socket_close.txt index 691cf57..eb42870 100644 --- a/site/doc/pages/socket_close.txt +++ b/site/doc/pages/socket_close.txt @@ -9,3 +9,7 @@ Closes an existing socket connection. :see >socket + +:related +**PHP:** PHP sockets and stream APIs like `socket_connect()`, `fread()`, `fwrite()`, and stream clients +**JavaScript / Node.js:** Node `net.Socket` connect, read, write, and close operations diff --git a/site/doc/pages/socket_connect.txt b/site/doc/pages/socket_connect.txt index b78f9c6..681ed77 100644 --- a/site/doc/pages/socket_connect.txt +++ b/site/doc/pages/socket_connect.txt @@ -11,3 +11,7 @@ Opens a socket connection to the given 'host' and 'port'. :see >socket + +:related +**PHP:** PHP sockets and stream APIs like `socket_connect()`, `fread()`, `fwrite()`, and stream clients +**JavaScript / Node.js:** Node `net.Socket` connect, read, write, and close operations diff --git a/site/doc/pages/socket_read.txt b/site/doc/pages/socket_read.txt index 7df62a8..e7b1319 100644 --- a/site/doc/pages/socket_read.txt +++ b/site/doc/pages/socket_read.txt @@ -12,3 +12,7 @@ Reads data from a socket connection. :see >socket + +:related +**PHP:** PHP sockets and stream APIs like `socket_connect()`, `fread()`, `fwrite()`, and stream clients +**JavaScript / Node.js:** Node `net.Socket` connect, read, write, and close operations diff --git a/site/doc/pages/socket_write.txt b/site/doc/pages/socket_write.txt index 5d2cb66..d86c591 100644 --- a/site/doc/pages/socket_write.txt +++ b/site/doc/pages/socket_write.txt @@ -11,3 +11,7 @@ Writes a string of 'data' to the given socket. :see >socket + +:related +**PHP:** PHP sockets and stream APIs like `socket_connect()`, `fread()`, `fwrite()`, and stream clients +**JavaScript / Node.js:** Node `net.Socket` connect, read, write, and close operations diff --git a/site/doc/pages/split.txt b/site/doc/pages/split.txt index 6e05b3d..0d4d943 100644 --- a/site/doc/pages/split.txt +++ b/site/doc/pages/split.txt @@ -11,3 +11,7 @@ Splits 'str' into multiple strings based on the given delimiter 'delim'. :see >string + +:related +**PHP:** `explode()` or `preg_split()` +**JavaScript / Node.js:** `String.prototype.split()` diff --git a/site/doc/pages/split_space.txt b/site/doc/pages/split_space.txt index 4b943ec..883fc11 100644 --- a/site/doc/pages/split_space.txt +++ b/site/doc/pages/split_space.txt @@ -10,3 +10,7 @@ Splits 'str' into multiple strings along any whitespace characters (multiple whi :see >string + +:related +**PHP:** Whitespace splitting via `preg_split(/\s+/, ...)` +**JavaScript / Node.js:** Whitespace splitting via regex with `split(/\\s+/)` diff --git a/site/doc/pages/split_utf8.txt b/site/doc/pages/split_utf8.txt index ee1dc88..c8d168a 100644 --- a/site/doc/pages/split_utf8.txt +++ b/site/doc/pages/split_utf8.txt @@ -18,3 +18,7 @@ If 'compound_characters' is true, split_utf8 will attempt to combine compound ch :see >string + +:related +**PHP:** `preg_split(//u, ...)`, `mb_*` helpers, or grapheme-aware libraries +**JavaScript / Node.js:** `Array.from(str)` or iterator-based Unicode-aware splitting diff --git a/site/doc/pages/task.txt b/site/doc/pages/task.txt index a23da4c..c7c675b 100644 --- a/site/doc/pages/task.txt +++ b/site/doc/pages/task.txt @@ -11,3 +11,7 @@ task() starts the 'exec_func' in a new process and returns that process' ID. If :see >task + +:related +**PHP:** Background-process patterns using `proc_open()`, queues, cron, or worker supervisors +**JavaScript / Node.js:** Node `child_process`, worker queues, timers, schedulers, and supervised background jobs diff --git a/site/doc/pages/task_kill.txt b/site/doc/pages/task_kill.txt index a4ed75b..35c12c2 100644 --- a/site/doc/pages/task_kill.txt +++ b/site/doc/pages/task_kill.txt @@ -13,3 +13,7 @@ Possible signal numbers are: SIGABND, SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, :see >task + +:related +**PHP:** Background-process patterns using `proc_open()`, queues, cron, or worker supervisors +**JavaScript / Node.js:** Node `child_process`, worker queues, timers, schedulers, and supervised background jobs diff --git a/site/doc/pages/task_pid.txt b/site/doc/pages/task_pid.txt index ca1dc37..0ffb97e 100644 --- a/site/doc/pages/task_pid.txt +++ b/site/doc/pages/task_pid.txt @@ -10,3 +10,7 @@ Checks whether a process with the given 'key' is running and returns its PID if :see >task + +:related +**PHP:** Background-process patterns using `proc_open()`, queues, cron, or worker supervisors +**JavaScript / Node.js:** Node `child_process`, worker queues, timers, schedulers, and supervised background jobs diff --git a/site/doc/pages/task_repeat.txt b/site/doc/pages/task_repeat.txt index ee5f39e..bbf923f 100644 --- a/site/doc/pages/task_repeat.txt +++ b/site/doc/pages/task_repeat.txt @@ -15,3 +15,7 @@ If a process with the same `key` is already running, `task_repeat()` does not st :see >task + +:related +**PHP:** Background-process patterns using `proc_open()`, queues, cron, or worker supervisors +**JavaScript / Node.js:** Node `child_process`, worker queues, timers, schedulers, and supervised background jobs diff --git a/site/doc/pages/time.txt b/site/doc/pages/time.txt index 63cfb45..558c404 100644 --- a/site/doc/pages/time.txt +++ b/site/doc/pages/time.txt @@ -9,3 +9,7 @@ Returns a 64 bit integer containing the current Unix timestamp. :see >time + +:related +**PHP:** `time()` +**JavaScript / Node.js:** `Math.floor(Date.now() / 1000)` or `Date.now()` depending on units diff --git a/site/doc/pages/time_format_local.txt b/site/doc/pages/time_format_local.txt index d7f57ed..872d733 100644 --- a/site/doc/pages/time_format_local.txt +++ b/site/doc/pages/time_format_local.txt @@ -123,3 +123,7 @@ Returns a formatted date. This is based on the Linux date() command. The formatt :see >time + +:related +**PHP:** `date()` and `DateTime` formatting in the server local timezone +**JavaScript / Node.js:** `Intl.DateTimeFormat`, `Date#toLocaleString()`, or date libraries diff --git a/site/doc/pages/time_format_utc.txt b/site/doc/pages/time_format_utc.txt index 7a870e6..789b844 100644 --- a/site/doc/pages/time_format_utc.txt +++ b/site/doc/pages/time_format_utc.txt @@ -123,3 +123,7 @@ Returns a formatted date in the GMT/UTC timezone. This is based on the Linux dat :see >time + +:related +**PHP:** `gmdate()` and UTC `DateTime` formatting +**JavaScript / Node.js:** `Date#toISOString()` and UTC formatting helpers diff --git a/site/doc/pages/time_parse.txt b/site/doc/pages/time_parse.txt index e0c2e06..81b8415 100644 --- a/site/doc/pages/time_parse.txt +++ b/site/doc/pages/time_parse.txt @@ -10,3 +10,7 @@ Attempts to parse the given 'time_string' into a Unix timestamp. :see >time + +:related +**PHP:** `strtotime()` or `DateTimeImmutable` parsing +**JavaScript / Node.js:** `Date.parse()` or date-library parsing helpers diff --git a/site/doc/pages/time_precise.txt b/site/doc/pages/time_precise.txt index c640f85..f77c191 100644 --- a/site/doc/pages/time_precise.txt +++ b/site/doc/pages/time_precise.txt @@ -9,3 +9,7 @@ Returns a 64 bit float containing the current Unix timestamp with millisecond ac :see >time + +:related +**PHP:** `microtime(true)` or high-resolution timers +**JavaScript / Node.js:** `performance.now()` or high-resolution Node timers diff --git a/site/doc/pages/to_lower.txt b/site/doc/pages/to_lower.txt index 714f606..bd03f04 100644 --- a/site/doc/pages/to_lower.txt +++ b/site/doc/pages/to_lower.txt @@ -12,3 +12,7 @@ Note: this function is not yet Unicode-aware. :see >string + +:related +**PHP:** `strtolower()` +**JavaScript / Node.js:** `String.prototype.toLowerCase()` diff --git a/site/doc/pages/to_upper.txt b/site/doc/pages/to_upper.txt index 2711fc0..c965fca 100644 --- a/site/doc/pages/to_upper.txt +++ b/site/doc/pages/to_upper.txt @@ -12,3 +12,7 @@ Note: this function is not yet Unicode-aware. :see >string + +:related +**PHP:** `strtoupper()` +**JavaScript / Node.js:** `String.prototype.toUpperCase()` diff --git a/site/doc/pages/trim.txt b/site/doc/pages/trim.txt index ed679ba..ec7adbe 100644 --- a/site/doc/pages/trim.txt +++ b/site/doc/pages/trim.txt @@ -10,3 +10,7 @@ Returns a string where leading an trailing whitespace characters have been trimm :see >string + +:related +**PHP:** `trim()` +**JavaScript / Node.js:** `String.prototype.trim()` diff --git a/site/doc/pages/unit_call.txt b/site/doc/pages/unit_call.txt index 3e60a6f..43a22ff 100644 --- a/site/doc/pages/unit_call.txt +++ b/site/doc/pages/unit_call.txt @@ -8,7 +8,11 @@ call_param : optional, call parameter return value : DTree* returned from function :desc -Calls an exported function inside a UCE file. +Calls an exported function inside another UCE file. + +Use `unit_call()` when you need a structured data exchange between units rather than rendered HTML output. + +The callee must expose an `EXPORT` function whose name matches `function_name`. Arguments are passed through `call_param`, and the return value is a `DTree*` owned by the callee. :Example // export a function @@ -20,5 +24,9 @@ EXPORT DTree* test_func(DTree* call_param) // use that function in another file unit_call("call_file_funcs.uce", "test_func"); +:related +**PHP:** `include`, `require`, or calling a function from an included module, especially when returning arrays or objects instead of rendering a view. +**JavaScript / Node.js:** Importing a module and calling an exported function, or dynamically loading a module and passing structured arguments. + :see >ob diff --git a/site/doc/pages/unit_compile.txt b/site/doc/pages/unit_compile.txt index ca1ecd8..cfaf0ab 100644 --- a/site/doc/pages/unit_compile.txt +++ b/site/doc/pages/unit_compile.txt @@ -13,3 +13,7 @@ If `path` is relative, it is resolved relative to the current executing unit. Su :see unit_info units_list + +:related +**PHP:** Template compilation or preloading steps that generate cached PHP artifacts +**JavaScript / Node.js:** Build-step compilation, bundling, or ahead-of-time template transforms diff --git a/site/doc/pages/unit_info.txt b/site/doc/pages/unit_info.txt index d200a9e..e2c1806 100644 --- a/site/doc/pages/unit_info.txt +++ b/site/doc/pages/unit_info.txt @@ -11,11 +11,12 @@ Returns runtime metadata for a UCE compilation unit. The returned tree includes: - normalized paths and generated artifact paths +- unit metadata sidecar paths plus runtime and compiled unit ABI version information - compile status, compile error status, runtime error status, and a combined `error_status` - request and invocation counters - best, worst, last, and average render time - compile counters plus best, worst, last, and average compile time -- file mtimes, stale status, load status, and exported API declarations +- file mtimes, stale status, ABI compatibility status, load status, and exported API declarations If `path` is relative, it is resolved relative to the current executing unit. @@ -25,3 +26,7 @@ Because unit metadata lives in process memory alongside `SharedUnit`, request an units_list unit_compile 0_context + +:related +**PHP:** Reflection-style metadata about included modules, callables, or exported capabilities +**JavaScript / Node.js:** Module metadata, reflection-like inspection, or manifest-based capability lookup diff --git a/site/doc/pages/unit_load.txt b/site/doc/pages/unit_load.txt index 4b20426..5625464 100644 --- a/site/doc/pages/unit_load.txt +++ b/site/doc/pages/unit_load.txt @@ -14,3 +14,7 @@ This is a low-level runtime helper. Most application code should prefer `unit_re unit_render unit_call load + +:related +**PHP:** `include`, `require`, autoloaders, and lazy-loading of modules +**JavaScript / Node.js:** Dynamic `import()`, lazy module loading, or require-on-demand patterns diff --git a/site/doc/pages/unit_render.txt b/site/doc/pages/unit_render.txt index 53c6d3c..3bf6587 100644 --- a/site/doc/pages/unit_render.txt +++ b/site/doc/pages/unit_render.txt @@ -20,3 +20,7 @@ unit_render("page-template.uce", context); :see >ob + +:related +**PHP:** Rendering another view or template with the current request state and captured output +**JavaScript / Node.js:** Calling another template renderer or SSR partial with the current request context diff --git a/site/doc/pages/units_list.txt b/site/doc/pages/units_list.txt index 560dd5c..92c7104 100644 --- a/site/doc/pages/units_list.txt +++ b/site/doc/pages/units_list.txt @@ -9,3 +9,7 @@ This includes the shared known-unit registry plus any units already loaded in th :see unit_info unit_compile + +:related +**PHP:** Enumerating available modules, templates, or plugin entrypoints +**JavaScript / Node.js:** Listing registered modules, routes, or dynamically discovered components diff --git a/site/doc/pages/uri_decode.txt b/site/doc/pages/uri_decode.txt index eb5df91..44a9c57 100644 --- a/site/doc/pages/uri_decode.txt +++ b/site/doc/pages/uri_decode.txt @@ -10,3 +10,7 @@ Decodes an URI-encoded string 's'. :see >uri + +:related +**PHP:** `urldecode()` or `rawurldecode()` +**JavaScript / Node.js:** `decodeURIComponent()` diff --git a/site/doc/pages/uri_encode.txt b/site/doc/pages/uri_encode.txt index 65cc486..6aaccfb 100644 --- a/site/doc/pages/uri_encode.txt +++ b/site/doc/pages/uri_encode.txt @@ -10,3 +10,7 @@ URI-encodes a string. :see >uri + +:related +**PHP:** `urlencode()` or `rawurlencode()` +**JavaScript / Node.js:** `encodeURIComponent()` diff --git a/site/doc/pages/var_dump.txt b/site/doc/pages/var_dump.txt index b469a7d..ed20a78 100644 --- a/site/doc/pages/var_dump.txt +++ b/site/doc/pages/var_dump.txt @@ -10,3 +10,7 @@ return value : string containing a human-friendly representation of 't' :desc Returns a string representation of 't' intended for debugging. + +:related +**PHP:** `var_dump()` and `print_r()` +**JavaScript / Node.js:** `console.log()`, `console.dir()`, and structured inspection helpers like `util.inspect()` diff --git a/site/doc/pages/ws_close.txt b/site/doc/pages/ws_close.txt index 3158ae9..dbc44bc 100644 --- a/site/doc/pages/ws_close.txt +++ b/site/doc/pages/ws_close.txt @@ -12,3 +12,7 @@ If `connection_id` is omitted, the current connection handled by `WS(Request& co :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_connection_count.txt b/site/doc/pages/ws_connection_count.txt index 9f0939a..367aa5d 100644 --- a/site/doc/pages/ws_connection_count.txt +++ b/site/doc/pages/ws_connection_count.txt @@ -12,3 +12,7 @@ If `scope` is omitted, the current page scope is used. :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_connection_id.txt b/site/doc/pages/ws_connection_id.txt index 13a7966..d0ea57d 100644 --- a/site/doc/pages/ws_connection_id.txt +++ b/site/doc/pages/ws_connection_id.txt @@ -11,3 +11,7 @@ This ID can be passed to `ws_send_to()` or `ws_close()` to target a single conne :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_connections.txt b/site/doc/pages/ws_connections.txt index b4390e3..8bb1924 100644 --- a/site/doc/pages/ws_connections.txt +++ b/site/doc/pages/ws_connections.txt @@ -12,3 +12,7 @@ If `scope` is omitted, the current page scope is used. :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_is_binary.txt b/site/doc/pages/ws_is_binary.txt index b48efad..cada785 100644 --- a/site/doc/pages/ws_is_binary.txt +++ b/site/doc/pages/ws_is_binary.txt @@ -11,3 +11,7 @@ If this returns `false`, the current message was delivered as a text frame. :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_message.txt b/site/doc/pages/ws_message.txt index aec50da..342fdf4 100644 --- a/site/doc/pages/ws_message.txt +++ b/site/doc/pages/ws_message.txt @@ -13,3 +13,7 @@ Use `ws_is_binary()` or `ws_opcode()` to decide how the payload should be interp :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_opcode.txt b/site/doc/pages/ws_opcode.txt index 66d2b53..a7179c2 100644 --- a/site/doc/pages/ws_opcode.txt +++ b/site/doc/pages/ws_opcode.txt @@ -14,3 +14,7 @@ Common values are: :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_scope.txt b/site/doc/pages/ws_scope.txt index 05676ab..eabf0fd 100644 --- a/site/doc/pages/ws_scope.txt +++ b/site/doc/pages/ws_scope.txt @@ -13,3 +13,7 @@ In the current runtime implementation this scope is the page's internal endpoint :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_send.txt b/site/doc/pages/ws_send.txt index 4aaa641..e835471 100644 --- a/site/doc/pages/ws_send.txt +++ b/site/doc/pages/ws_send.txt @@ -14,3 +14,7 @@ If `scope` is omitted, the current page scope is used. :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/pages/ws_send_to.txt b/site/doc/pages/ws_send_to.txt index ef0a35a..340d69f 100644 --- a/site/doc/pages/ws_send_to.txt +++ b/site/doc/pages/ws_send_to.txt @@ -12,3 +12,7 @@ Queues a WebSocket message for one specific connected client. :see >websocket + +:related +**PHP:** WebSocket connection metadata and send APIs in Ratchet, Workerman, or similar server frameworks +**JavaScript / Node.js:** Browser `WebSocket` properties and Node `ws` server-side connection inspection and send methods diff --git a/site/doc/search.uce b/site/doc/search.uce index 35128df..7d93091 100644 --- a/site/doc/search.uce +++ b/site/doc/search.uce @@ -5,6 +5,14 @@ bool str_contains(String haystack, String needle) return replace(haystack, needle, "\x01") != haystack; } +void render_hit(String ft, String name, String badge, String snippet) +{ + <>
+ +
+} + RENDER(Request& context) { String q = to_lower(trim(context.get["q"])); @@ -14,7 +22,16 @@ RENDER(Request& context) return; } - u32 count = 0; + // Collect results into two buckets: title matches first, then content-only matches + StringList title_ft; + StringList title_name; + StringList title_badge; + StringList title_snippet; + + StringList body_ft; + StringList body_name; + StringList body_badge; + StringList body_snippet; for(auto file_name : ls("pages/")) { @@ -34,34 +51,56 @@ RENDER(Request& context) } String content = file_get_contents("pages/" + ft + ".txt"); + bool name_match = str_contains(to_lower(name), q); + bool content_match = str_contains(to_lower(content), q); - if(str_contains(to_lower(name), q) || str_contains(to_lower(content), q)) + if(!name_match && !content_match) + continue; + + // Find first content line that contains the query for a snippet + String snippet = ""; + if(!name_match || content_match) { - // Find first content line that contains the query for a snippet - String snippet = ""; for(auto line : split(content, "\n")) { String t = trim(line); if(t.length() > 4 && t.substr(0, 1) != ":" && str_contains(to_lower(t), q)) { snippet = t; - if(snippet.length() > 100) - snippet = snippet.substr(0, 100) + "…"; + if(snippet.length() > 120) + snippet = snippet.substr(0, 120) + "…"; break; } } + } - <>
- -
-
- - count += 1; + if(name_match) + { + title_ft.push_back(ft); + title_name.push_back(name); + title_badge.push_back(badge); + title_snippet.push_back(snippet); + } + else + { + body_ft.push_back(ft); + body_name.push_back(name); + body_badge.push_back(badge); + body_snippet.push_back(snippet); } } + u32 count = title_ft.size() + body_ft.size(); + if(count == 0) { <>

No results for ""

+ return; } + + for(u32 i = 0; i < title_ft.size(); i++) + render_hit(title_ft[i], title_name[i], title_badge[i], title_snippet[i]); + + for(u32 i = 0; i < body_ft.size(); i++) + render_hit(body_ft[i], body_name[i], body_badge[i], body_snippet[i]); } diff --git a/site/doc/singlepage.uce b/site/doc/singlepage.uce index b39ef47..a0a739d 100644 --- a/site/doc/singlepage.uce +++ b/site/doc/singlepage.uce @@ -42,6 +42,10 @@ void render_doc_page(String page) { <>

Description

} + else if(layout_class == "related") + { + <>

Related PHP and JS

+ } else if(layout_class == "see") { @@ -79,7 +83,7 @@ void render_doc_page(String page) } else { - <>
+ <>
} } } diff --git a/site/doc/style.css b/site/doc/style.css index 0ad307c..8eefed1 100644 --- a/site/doc/style.css +++ b/site/doc/style.css @@ -431,32 +431,42 @@ details pre { } #search-results { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); - gap: 4px; + display: flex; + flex-direction: column; + gap: 2px; } #search-results .no-results { - grid-column: 1 / -1; color: var(--text-dim); padding: 12px 0; } .search-hit { display: flex; - flex-direction: column; - gap: 3px; + align-items: baseline; + gap: 10px; + padding: 5px 12px; + border-radius: 6px; + transition: background 120ms var(--ease); +} + +.search-hit:hover { + background: var(--bg-surface-hover); +} + +.search-hit a { + flex-shrink: 0; + font-family: var(--font-mono); + font-size: 0.92rem; } .search-snippet { - font-size: 0.75rem; - color: var(--text-dim); - font-family: var(--font-mono); - padding: 0 12px 6px; - line-height: 1.4; + font-size: 0.78rem; + color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + min-width: 0; } /* ── Scrollbar ── */ diff --git a/site/test/dtree.uce b/site/test/dtree.uce index 42a7bcd..b8e7bb7 100644 --- a/site/test/dtree.uce +++ b/site/test/dtree.uce @@ -3,6 +3,12 @@ RENDER(Request& context) { DTree t; + DTree assoc_left; + DTree assoc_right; + DTree assoc_merged; + DTree list_left; + DTree list_right; + DTree list_merged; <> @@ -47,6 +53,40 @@ RENDER(Request& context) ?> + Array Merge + +
+ Params
diff --git a/site/test/string.uce b/site/test/string.uce index 99e8787..9283598 100644 --- a/site/test/string.uce +++ b/site/test/string.uce @@ -5,7 +5,7 @@ RENDER(Request& context) { - DTree t; + String sample = " Hello UCE World "; <> @@ -16,7 +16,14 @@ RENDER(Request& context)
diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp index dda5326..8a0108a 100644 --- a/src/lib/compiler.cpp +++ b/src/lib/compiler.cpp @@ -1,19 +1,112 @@ #include "compiler.h" #include +#include #include #include namespace { +const char* UCE_SETUP_SYMBOL = "__uce_set_current_request"; +const char* UCE_RENDER_SYMBOL = "__uce_render"; +const char* UCE_COMPONENT_SYMBOL = "__uce_component"; +const char* UCE_WEBSOCKET_SYMBOL = "__uce_websocket"; +const u64 UCE_UNIT_ABI_VERSION = 1; + struct SharedUnitFilesystemState { bool source_exists = false; + bool metadata_exists = false; + bool metadata_parsed = false; + bool abi_compatible = false; time_t source_time = 0; time_t compiled_time = 0; + time_t metadata_time = 0; time_t setup_template_time = 0; + time_t compiler_header_time = 0; + time_t compiler_source_time = 0; + time_t runtime_binary_time = 0; + time_t compiler_abi_time = 0; time_t required_time = 0; + u64 metadata_abi_version = 0; + u64 runtime_abi_version = UCE_UNIT_ABI_VERSION; }; +bool compiler_is_u64_string(String value) +{ + value = trim(value); + if(value == "") + return(false); + for(auto c : value) + { + if(c < '0' || c > '9') + return(false); + } + return(true); +} + +bool compiler_parse_unit_metadata_abi(String content, u64& abi_out) +{ + content = trim(content); + if(content == "") + return(false); + + auto lines = split(content, "\n"); + for(auto& raw_line : lines) + { + auto line = trim(raw_line); + if(line == "") + continue; + auto split_pos = line.find("="); + if(split_pos == String::npos) + continue; + auto key = trim(line.substr(0, split_pos)); + auto value = trim(line.substr(split_pos + 1)); + if(key != "unit_abi_version" || !compiler_is_u64_string(value)) + continue; + abi_out = (u64)atoll(value.c_str()); + return(true); + } + return(false); +} + +String compiler_unit_metadata_text() +{ + return( + "format=uce-unit-metadata-v1\n" + "unit_abi_version=" + std::to_string(UCE_UNIT_ABI_VERSION) + "\n" + ); +} + +bool shared_unit_filesystem_requires_recompile(const SharedUnitFilesystemState& state) +{ + if(!state.source_exists) + return(true); + if(state.compiled_time == 0) + return(true); + if(state.compiled_time < state.required_time) + return(true); + if(!state.metadata_exists) + return(true); + if(!state.metadata_parsed) + return(true); + if(!state.abi_compatible) + return(true); + return(false); +} + +time_t compiler_runtime_abi_time(Request* context) +{ + if(!context || !context->server) + return(0); + + String root = context->server->config["COMPILER_SYS_PATH"]; + return(std::max({ + file_mtime(root + "/src/lib/compiler.h"), + file_mtime(root + "/src/lib/compiler.cpp"), + file_mtime(root + "/bin/uce_fastcgi.linux.bin") + })); +} + String compiler_registry_file_name(Request* context) { return(context->server->config["BIN_DIRECTORY"] + "/known-uce-files.txt"); @@ -109,7 +202,19 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share state.setup_template_time = file_mtime( context->server->config["COMPILER_SYS_PATH"] + "/" + context->server->config["SETUP_TEMPLATE"]); - state.required_time = std::max(state.source_time, state.setup_template_time); + state.compiler_header_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/src/lib/compiler.h"); + state.compiler_source_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/src/lib/compiler.cpp"); + state.runtime_binary_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/bin/uce_fastcgi.linux.bin"); + state.compiler_abi_time = compiler_runtime_abi_time(context); + state.metadata_time = file_mtime(su->meta_file_name); + state.metadata_exists = (state.metadata_time != 0); + if(state.metadata_exists) + state.metadata_parsed = compiler_parse_unit_metadata_abi( + file_get_contents(su->meta_file_name), + state.metadata_abi_version + ); + state.abi_compatible = (state.metadata_parsed && state.metadata_abi_version == state.runtime_abi_version); + state.required_time = std::max({state.source_time, state.setup_template_time, state.compiler_abi_time}); state.compiled_time = file_mtime(su->so_name); return(state); } @@ -120,11 +225,7 @@ bool shared_unit_cache_is_stale(Request* context, SharedUnit* su) return(true); auto state = inspect_shared_unit_filesystem(context, su); - if(!state.source_exists) - return(true); - if(state.compiled_time == 0) - return(true); - if(state.compiled_time < state.required_time) + if(shared_unit_filesystem_requires_recompile(state)) return(true); if(su->last_compiled != 0 && state.compiled_time != su->last_compiled) return(true); @@ -251,9 +352,9 @@ String compiler_status_from_filesystem(const SharedUnitFilesystemState& state, S { if(!state.source_exists) return("missing_source"); - if(state.compiled_time == 0) + if(state.compiled_time == 0 || !state.metadata_exists) return("not_compiled"); - if(state.compiled_time < state.required_time) + if(shared_unit_filesystem_requires_recompile(state)) return("stale"); if(su && su->so_handle) return("loaded"); @@ -506,7 +607,7 @@ String preprocess_named_render_syntax(String content) String render_name = trim(signature.substr(0, open_paren_pos)); String render_signature = signature.substr(open_paren_pos); if(render_name != "") - line = indent + "EXPORT void __unit_render_" + safe_name(render_name) + render_signature; + line = indent + "EXPORT void " + String(UCE_RENDER_SYMBOL) + "_" + safe_name(render_name) + render_signature; } } else if(trimmed.rfind("COMPONENT:", 0) == 0) @@ -518,7 +619,7 @@ String preprocess_named_render_syntax(String content) String render_name = trim(signature.substr(0, open_paren_pos)); String render_signature = signature.substr(open_paren_pos); if(render_name != "") - line = indent + "EXPORT void __unit_component_" + safe_name(render_name) + render_signature; + line = indent + "EXPORT void " + String(UCE_COMPONENT_SYMBOL) + "_" + safe_name(render_name) + render_signature; } } @@ -670,6 +771,7 @@ void setup_unit_paths(Request* context, SharedUnit* su, String file_name) su->so_name = su->bin_path + "/" + su->bin_file_name; su->api_file_name = su->bin_path + "/" + su->src_file_name + ".exports.txt"; + su->meta_file_name = su->bin_path + "/" + su->src_file_name + ".meta.txt"; //su->setup_file_name = su->bin_path + "/" + su->src_file_name + ".setup.h"; } @@ -707,14 +809,14 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name) su->compile_status = "loaded"; su->compile_error_status = ""; char *error; - su->on_setup = (request_handler)dlsym(su->so_handle, "set_current_request"); + su->on_setup = (request_handler)dlsym(su->so_handle, UCE_SETUP_SYMBOL); if ((error = dlerror()) != NULL) printf("Error - %s in %s\n", error, su->file_name.c_str()); - su->on_render = (request_ref_handler)dlsym(su->so_handle, "__unit_render"); + su->on_render = (request_ref_handler)dlsym(su->so_handle, UCE_RENDER_SYMBOL); dlerror(); - su->on_component = (request_ref_handler)dlsym(su->so_handle, "__unit_component"); + su->on_component = (request_ref_handler)dlsym(su->so_handle, UCE_COMPONENT_SYMBOL); dlerror(); - su->on_websocket = (request_ref_handler)dlsym(su->so_handle, "__unit_websocket"); + su->on_websocket = (request_ref_handler)dlsym(su->so_handle, UCE_WEBSOCKET_SYMBOL); dlerror(); su->api_declarations = split(file_get_contents(su->api_file_name), "\n"); //else @@ -777,6 +879,8 @@ void compile_shared_unit(Request* context, SharedUnit* su, String file_name) else { load_shared_unit(context, su, file_name); + if(su->so_handle) + file_put_contents(su->meta_file_name, compiler_unit_metadata_text()); compiler_record_compile_result( su, time_precise() - comp_start, @@ -1055,6 +1159,7 @@ DTree unit_info(String path) info["pre_file_name"] = su->pre_file_name; info["so_name"] = su->so_name; info["api_file_name"] = su->api_file_name; + info["meta_file_name"] = su->meta_file_name; info["compile_status"] = (su->compile_status != "unknown" ? su->compile_status : compiler_status_from_filesystem(fs_state, su)); info["compile_error_status"] = su->compile_error_status; info["runtime_error_status"] = su->runtime_error_status; @@ -1080,14 +1185,20 @@ DTree unit_info(String path) info["worst_render_time"] = su->worst_render_duration; info["source_mtime"] = (f64)fs_state.source_time; info["compiled_mtime"] = (f64)fs_state.compiled_time; + info["metadata_mtime"] = (f64)fs_state.metadata_time; info["setup_template_mtime"] = (f64)fs_state.setup_template_time; info["required_mtime"] = (f64)fs_state.required_time; + info["runtime_abi_version"] = (f64)fs_state.runtime_abi_version; + info["metadata_abi_version"] = (f64)fs_state.metadata_abi_version; compiler_tree_set_bool(info, "known", context->server->known_unit_files[resolved_path]); compiler_tree_set_bool(info, "current_unit", resolved_path == compiler_current_unit_path(context)); compiler_tree_set_bool(info, "loaded", su->so_handle != 0); compiler_tree_set_bool(info, "source_exists", fs_state.source_exists); compiler_tree_set_bool(info, "compiled_exists", fs_state.compiled_time != 0); - compiler_tree_set_bool(info, "stale", fs_state.source_exists && fs_state.compiled_time != 0 && fs_state.compiled_time < fs_state.required_time); + compiler_tree_set_bool(info, "metadata_exists", fs_state.metadata_exists); + compiler_tree_set_bool(info, "metadata_parsed", fs_state.metadata_parsed); + compiler_tree_set_bool(info, "abi_compatible", fs_state.abi_compatible); + compiler_tree_set_bool(info, "stale", shared_unit_cache_is_stale(context, su)); compiler_tree_set_bool(info, "has_render", su->on_render != 0); compiler_tree_set_bool(info, "has_component", su->on_component != 0); compiler_tree_set_bool(info, "has_websocket", su->on_websocket != 0); @@ -1225,22 +1336,22 @@ String page_render_handler_symbol(String render_name) { render_name = trim(render_name); if(render_name == "" || render_name == "render") - return("__unit_render"); - return("__unit_render_" + safe_name(render_name)); + return(UCE_RENDER_SYMBOL); + return(String(UCE_RENDER_SYMBOL) + "_" + safe_name(render_name)); } String component_handler_symbol(String render_name) { render_name = trim(render_name); if(render_name == "") - return("__unit_component"); - return("__unit_component_" + safe_name(render_name)); + return(UCE_COMPONENT_SYMBOL); + return(String(UCE_COMPONENT_SYMBOL) + "_" + safe_name(render_name)); } request_ref_handler get_page_render_handler(SharedUnit* su, String render_name) { String symbol = page_render_handler_symbol(render_name); - if(symbol == "__unit_render") + if(symbol == UCE_RENDER_SYMBOL) return(su->on_render); auto it = su->api_functions.find(symbol); @@ -1256,7 +1367,7 @@ request_ref_handler get_page_render_handler(SharedUnit* su, String render_name) request_ref_handler get_component_handler(SharedUnit* su, String render_name) { String symbol = component_handler_symbol(render_name); - if(symbol == "__unit_component") + if(symbol == UCE_COMPONENT_SYMBOL) return(su->on_component); auto it = su->api_functions.find(symbol); @@ -1278,7 +1389,7 @@ bool compiler_invoke_render(Request* context, String file_name, String render_na if(!su->on_setup) { if(error_out) - *error_out = "internal error: set_current_request() not defined in " + file_name; + *error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + file_name; return(false); } @@ -1327,7 +1438,7 @@ bool compiler_invoke_component(Request* context, String file_name, String render if(!su->on_setup) { if(error_out) - *error_out = "internal error: set_current_request() not defined in " + file_name; + *error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + file_name; return(false); } @@ -1386,7 +1497,7 @@ void compiler_invoke_websocket(Request* context, String file_name) if(!su->on_setup) { - printf("internal error: set_current_request() not defined in %s\n", file_name.c_str()); + printf("internal error: %s() not defined in %s\n", UCE_SETUP_SYMBOL, file_name.c_str()); return; } @@ -1529,7 +1640,7 @@ DTree* unit_call(String file_name, String function_name, DTree* call_param) { if(!su->on_setup) { - print("internal error: set_current_request() not defined in", file_name, "\n"); + print("internal error: ", UCE_SETUP_SYMBOL, "() not defined in ", file_name, "\n"); } else { diff --git a/src/lib/compiler.h b/src/lib/compiler.h index 41c8537..0f2a57c 100644 --- a/src/lib/compiler.h +++ b/src/lib/compiler.h @@ -1,8 +1,8 @@ #pragma once -#define RENDER(X) extern "C" void __unit_render(Request& context) -#define COMPONENT(X) extern "C" void __unit_component(Request& context) -#define WS(X) extern "C" void __unit_websocket(Request& context) +#define RENDER(X) extern "C" void __uce_render(Request& context) +#define COMPONENT(X) extern "C" void __uce_component(Request& context) +#define WS(X) extern "C" void __uce_websocket(Request& context) #define EXPORT extern "C" String process_html_literal(Request* context, SharedUnit* su, String content); diff --git a/src/lib/functionlib.cpp b/src/lib/functionlib.cpp index ebde05e..96d1d32 100644 --- a/src/lib/functionlib.cpp +++ b/src/lib/functionlib.cpp @@ -54,6 +54,82 @@ String to_upper(String s) return(result); } +String substr(String s, s64 start_pos) +{ + s64 len = s.length(); + s64 start = start_pos; + if(start < 0) + start = len + start; + if(start < 0) + start = 0; + if(start >= len) + return(""); + return(s.substr(start)); +} + +String substr(String s, s64 start_pos, s64 length) +{ + s64 len = s.length(); + s64 start = start_pos; + if(start < 0) + start = len + start; + if(start < 0) + start = 0; + if(start >= len) + return(""); + + s64 end = len; + if(length >= 0) + end = start + length; + else + end = len + length; + + if(end < start) + return(""); + if(end > len) + end = len; + return(s.substr(start, end - start)); +} + +s64 strpos(String haystack, String needle, s64 offset) +{ + s64 start = offset; + s64 len = haystack.length(); + if(start < 0) + start = len + start; + if(start < 0) + start = 0; + if(start > len) + return(-1); + if(needle == "") + return(start); + auto pos = haystack.find(needle, start); + if(pos == std::string::npos) + return(-1); + return(pos); +} + +bool str_starts_with(String haystack, String needle) +{ + if(needle.length() > haystack.length()) + return(false); + return(haystack.compare(0, needle.length(), needle) == 0); +} + +bool str_ends_with(String haystack, String needle) +{ + if(needle.length() > haystack.length()) + return(false); + return(haystack.compare(haystack.length() - needle.length(), needle.length(), needle) == 0); +} + +bool contains(String haystack, String needle) +{ + if(needle == "") + return(true); + return(haystack.find(needle) != std::string::npos); +} + String replace(String s, String search, String replace_with) { s64 last_spos = 0; @@ -241,6 +317,58 @@ String join(StringList l, String delim) return(result); } +namespace { + +bool array_merge_key_is_index(String key) +{ + if(key == "") + return(false); + for(auto c : key) + { + if(!isdigit(c)) + return(false); + } + return(true); +} + +} + +StringMap array_merge(StringMap a, StringMap b) +{ + StringMap result = a; + for(const auto& entry : b) + result[entry.first] = entry.second; + return(result); +} + +DTree array_merge(DTree a, DTree b) +{ + const DTree& left = a.deref(); + const DTree& right = b.deref(); + if(left.type != 'M') + return(b); + + DTree result; + result.set(a); + if(right.type != 'M') + return(result); + + bool append_numeric_keys = left.is_list() || right.is_list(); + for(const auto& entry : right._map) + { + if(append_numeric_keys && array_merge_key_is_index(entry.first)) + { + DTree child = entry.second; + result.push(child); + } + else + { + result[entry.first] = entry.second; + } + } + return(result); +} + StringList split_utf8(String s, bool compound_characters) { StringList result; diff --git a/src/lib/functionlib.h b/src/lib/functionlib.h index f7d62f5..ff54ee3 100644 --- a/src/lib/functionlib.h +++ b/src/lib/functionlib.h @@ -7,6 +7,12 @@ u64 int_val(String s, u32 base = 10); f64 float_val(String s); String to_lower(String s); String to_upper(String s); +String substr(String s, s64 start_pos); +String substr(String s, s64 start_pos, s64 length); +s64 strpos(String haystack, String needle, s64 offset = 0); +bool str_starts_with(String haystack, String needle); +bool str_ends_with(String haystack, String needle); +bool contains(String haystack, String needle); String replace(String s, String search, String replace_with); String trim(String raw); @@ -86,6 +92,8 @@ DTree json_decode(String s); String var_dump(StringMap map, String prefix = "", String postfix = "\n"); String var_dump(StringList slist, String prefix = "", String postfix = "\n"); +StringMap array_merge(StringMap a, StringMap b); +DTree array_merge(DTree a, DTree b); void ob_start(); void ob_close(); diff --git a/src/lib/types.h b/src/lib/types.h index 325d794..9903617 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -65,6 +65,7 @@ struct SharedUnit { String file_name; String so_name; String api_file_name; + String meta_file_name; String setup_file_name; StringList api_declarations; std::map api_functions; diff --git a/src/lib/uri.cpp b/src/lib/uri.cpp index 2408521..d386f59 100644 --- a/src/lib/uri.cpp +++ b/src/lib/uri.cpp @@ -256,6 +256,12 @@ String encode_query(StringMap map) return(result); } +void redirect(String url, s32 code) +{ + context->header["Location"] = url; + context->set_status(code); +} + String trim_wrapping_quotes(String raw) { if(raw.length() >= 2 && raw[0] == '"' && raw[raw.length()-1] == '"') diff --git a/src/lib/uri.h b/src/lib/uri.h index ad4ee2a..c3ae573 100644 --- a/src/lib/uri.h +++ b/src/lib/uri.h @@ -7,6 +7,7 @@ String uri_decode(String q); String uri_encode(String q); StringMap parse_query(String q); String encode_query(StringMap map); +void redirect(String url, s32 code = 302); StringMap parse_multipart(String q, String boundary, std::vector& uploaded_files); URI parse_uri(String uri_String); void set_cookie(