Variable PROBE_RUNTIME_PHPConst
PROBE_RUNTIME_PHP: "// __CFW_HOST_HELPERS__\n\n$out = [];\n$out['vrzno_env'] = function_exists('vrzno_env');\n$exec = cfw_host('cfwSqlExec');\n$txn = cfw_host('cfwSqlTxn');\n\n// Question 6: does vrzno_env() return something PHP can invoke as $fn($json)?\n// The driver accepts any object and lets the call fail rather than gating on\n// is_callable(), precisely because this was unverified.\n$out['bridge'] = [\n\t'execType' => get_debug_type($exec),\n\t'txnType' => get_debug_type($txn),\n\t'execIsCallable' => is_callable($exec),\n\t'txnIsCallable' => is_callable($txn),\n\t'execIsObject' => is_object($exec),\n];\n\n$probe = function ($label, $sql, $params = []) use (&$out) {\n\t$r = cfw_sql($sql, $params);\n\t$out['q'][$label] = [\n\t\t'sql' => $sql,\n\t\t'ok' => ($r['ok'] ?? false) === true,\n\t\t'error' => $r['error'] ?? null,\n\t\t'rows' => array_slice($r['rows'] ?? [], 0, 6),\n\t\t'rowCount' => count($r['rows'] ?? []),\n\t];\n\treturn $out['q'][$label]['ok'];\n};\n\n// a table to introspect\n$probe('setup_drop', 'DROP TABLE IF EXISTS cfw_probe');\n$probe('setup_create', 'CREATE TABLE cfw_probe (x INTEGER PRIMARY KEY, t TEXT, big INTEGER)');\n$probe('setup_insert', 'INSERT INTO cfw_probe (x, t, big) VALUES (1, :t, 0)', [':t' => 'hello']);\n\n// Q1: PRAGMA table_info -- the inherited Schema introspection needs it\n$probe('pragma_table_info', 'PRAGMA table_info(cfw_probe)');\n\n// Q2: PRAGMA index_list\n$probe('create_index', 'CREATE INDEX cfw_probe_t ON cfw_probe (t)');\n$probe('pragma_index_list', 'PRAGMA index_list(cfw_probe)');\n$probe('pragma_index_info', 'PRAGMA index_info(cfw_probe_t)');\n\n// Q3: schema-qualified sqlite_master, which findTables() emits\n$probe('qualified_master', 'SELECT name FROM \"main\".sqlite_master WHERE type = :t ORDER BY name', [\n\t':t' => 'table',\n]);\n$probe('bare_master', 'SELECT name FROM sqlite_master WHERE type = :t ORDER BY name', [\n\t':t' => 'table',\n]);\n\n// Q4: CREATE TEMPORARY TABLE, which queryTemporary() needs\n$probe('temp_create', 'CREATE TEMPORARY TABLE cfw_tmp (x INTEGER)');\n$probe('temp_insert', 'INSERT INTO cfw_tmp (x) VALUES (42)');\n$probe('temp_select', 'SELECT x FROM cfw_tmp');\n\n// Q5: schema-qualified index name, which Schema::createIndexSql() emits\n$probe('qualified_index', 'CREATE INDEX \"main\".\"cfw_probe_q\" ON cfw_probe (x)');\n$probe('qualified_index_bare', 'CREATE INDEX main.cfw_probe_q2 ON cfw_probe (t, x)');\n\n// Q6 continued: the SQLite version and every builtin the function audit assumed\n$fns = [\n\t'version' => 'SELECT sqlite_version() AS v',\n\t'concat' => \"SELECT concat('a','b') AS v\",\n\t'concat_ws' => \"SELECT concat_ws('-','a','b') AS v\",\n\t'pow' => 'SELECT pow(2,3) AS v',\n\t'exp' => 'SELECT exp(1) AS v',\n\t'iif' => 'SELECT iif(1,2,3) AS v',\n\t'max_variadic' => 'SELECT max(1,2,3) AS v',\n\t'min_variadic' => 'SELECT min(3,2,1) AS v',\n\t'random' => 'SELECT random() IS NOT NULL AS v',\n\t'substr' => \"SELECT substr('abcdef',2,3) AS v\",\n\t'substring' => \"SELECT substring('abcdef',2,3) AS v\",\n\t'length_chars' => \"SELECT length('naive') AS v\",\n\t'md5' => \"SELECT md5('a') AS v\",\n\t'regexp' => \"SELECT 'abc' REGEXP 'b' AS v\",\n\t'nocase_eq' => \"SELECT ('Hello' = 'hello' COLLATE NOCASE) AS v\",\n\t'nocase_utf8' => \"SELECT ('A' = 'a' COLLATE NOCASE_UTF8) AS v\",\n];\nforeach ($fns as $label => $sql) {\n\t$probe('fn_' . $label, $sql);\n}\n\n// A version ladder, because the engine refuses to report its own version and\n// Drupal 11.4.5 gates installation on SQLite >= 3.45. Each row is a feature that\n// landed in exactly one release, so the highest passing row is a proven floor.\n$ladder = [\n\t'3.32' => 'SELECT iif(1,2,3) AS v',\n\t'3.35' => 'SELECT pow(2,3) AS v',\n\t'3.38' => \"SELECT ('{\\\"a\\\":1}' ->> '$.a') AS v\",\n\t'3.44' => \"SELECT concat('a','b') AS v\",\n\t'3.45' => \"SELECT hex(jsonb('{\\\"a\\\":1}')) AS v\",\n\t'3.46' => \"SELECT unhex('41') AS v\",\n];\n$floor = null;\nforeach ($ladder as $release => $sql) {\n\tif ($probe('ver_' . $release, $sql)) {\n\t\t$floor = $release;\n\t}\n}\n$out['versionFloor'] = $floor;\n$out['meetsDrupalMinimum'] = $floor !== null && version_compare($floor . '.0', '3.45', '>=');\n\n// builtin GLOB semantics, which decide whether likeToGlob() can be wired in\n$globs = [\n\t'glob_star' => \"SELECT ('abc' GLOB 'a*') AS v\",\n\t'glob_question' => \"SELECT ('abc' GLOB 'a?c') AS v\",\n\t'glob_percent_literal' => \"SELECT ('a%c' GLOB 'a%c') AS v\",\n\t'glob_percent_not_wildcard' => \"SELECT ('abc' GLOB 'a%c') AS v\",\n\t'glob_bracket_quote_star' => \"SELECT ('a*c' GLOB 'a[*]c') AS v\",\n\t'glob_bracket_quote_question' => \"SELECT ('a?c' GLOB 'a[?]c') AS v\",\n\t'glob_bracket_quote_bracket' => \"SELECT ('a[c' GLOB 'a[[]c') AS v\",\n\t'glob_case_sensitive' => \"SELECT ('ABC' GLOB 'abc') AS v\",\n\t'glob_with_escape_clause' => \"SELECT ('abc' GLOB 'abc' ESCAPE '\\\\') AS v\",\n\t'like_case_insensitive' => \"SELECT ('ABC' LIKE 'abc') AS v\",\n\t'like_with_escape_clause' => \"SELECT ('a%c' LIKE 'a\\\\%c' ESCAPE '\\\\') AS v\",\n];\nforeach ($globs as $label => $sql) {\n\t$probe($label, $sql);\n}\n\n// Q7: does sql.exec() bind a JS BigInt? The codec produces one for an integer\n// beyond Number.MAX_SAFE_INTEGER, which JSON cannot carry and a JS number cannot\n// hold exactly, so the envelope is the only way the value travels at all.\n$wide = '9007199254740993';\n$probe('bigint_write_envelope', 'UPDATE cfw_probe SET big = :b WHERE x = 1', [\n\t':b' => ['__phpint' => $wide],\n]);\n$probe('bigint_read_envelope', 'SELECT big FROM cfw_probe WHERE x = 1');\n\n// the fallback the driver has to use if a BigInt cannot be bound: a decimal\n// string, relying on the column's INTEGER affinity to convert it\n$probe('bigint_write_string', 'UPDATE cfw_probe SET big = :b WHERE x = 1', [':b' => $wide]);\n$probe('bigint_read_string', 'SELECT big FROM cfw_probe WHERE x = 1');\n$probe(\n\t'bigint_typeof',\n\t'SELECT typeof(big) AS t, big + 0 AS n, CAST(big AS TEXT) AS s FROM cfw_probe WHERE x = 1',\n);\n$probe('bigint_match_string', 'SELECT COUNT(*) AS c FROM cfw_probe WHERE big = :b', [\n\t':b' => $wide,\n]);\n$probe('bigint_max', 'UPDATE cfw_probe SET big = :b WHERE x = 1', [':b' => '9223372036854775807']);\n$probe('bigint_max_read', 'SELECT CAST(big AS TEXT) AS s FROM cfw_probe WHERE x = 1');\n\n$resolve = function ($v) {\n\treturn is_array($v) ? $v['__phpint'] ?? json_encode($v) : $v;\n};\n$out['bigint'] = [\n\t'sent' => $wide,\n\t'envelopeBindOk' => $out['q']['bigint_write_envelope']['ok'],\n\t'envelopeBindError' => $out['q']['bigint_write_envelope']['error'],\n\t'stringBindOk' => $out['q']['bigint_write_string']['ok'],\n\t'readBackRaw' => $resolve($out['q']['bigint_read_string']['rows'][0]['big'] ?? null),\n\t'readBackExact' =>\n\t\t(string) $resolve($out['q']['bigint_read_string']['rows'][0]['big'] ?? null) === $wide,\n\t'storedType' => $out['q']['bigint_typeof']['rows'][0]['t'] ?? null,\n\t'castToTextExact' =>\n\t\t(string) $resolve($out['q']['bigint_typeof']['rows'][0]['s'] ?? null) === $wide,\n\t'matchedByStringBind' =>\n\t\t(string) $resolve($out['q']['bigint_match_string']['rows'][0]['c'] ?? null) === '1',\n\t'int64MaxCastExact' =>\n\t\t(string) $resolve($out['q']['bigint_max_read']['rows'][0]['s'] ?? null) ===\n\t\t'9223372036854775807',\n];\n\n// the transaction bridge, against real storage rather than a PDO stand-in\n$t = cfw_txn(\n\t[\n\t\t[\n\t\t\t'sql' => 'INSERT INTO cfw_probe (x, t, big) VALUES (2, :t, 0)',\n\t\t\t'params' => [':t' => 'speculative'],\n\t\t],\n\t],\n\tfalse,\n\t['sql' => 'SELECT COUNT(*) AS c FROM cfw_probe', 'params' => []],\n);\n$after = cfw_sql('SELECT COUNT(*) AS c FROM cfw_probe');\n$out['txn'] = [\n\t'ok' => ($t['ok'] ?? false) === true,\n\t'error' => $t['error'] ?? null,\n\t'speculativeCount' => $t['readResult']['rows'][0]['c'] ?? null,\n\t'committedCount' => $after['rows'][0]['c'] ?? null,\n\t'leftNothingBehind' =>\n\t\t($t['readResult']['rows'][0]['c'] ?? null) !== ($after['rows'][0]['c'] ?? null),\n];\n\n$probe('cleanup', 'DROP TABLE IF EXISTS cfw_probe');\n\n$out['phpVersion'] = PHP_VERSION;\n$out['intSize'] = PHP_INT_SIZE;\necho json_encode($out);\n"