Exception:Array
(
[message] => You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@SQL_LIMIT(?, ?)
) AS post
FROM taxonomy_item AS categorie...' at line 22
SELECT DISTINCT categories.taxonomy_item_id, @taxonomy_item_id := categories.taxonomy_item_id, categories.*, tc.*, tc.content as content, categories.taxonomy_item_id as array_key
,(SELECT JSON_ARRAYAGG( JSON_OBJECT('post_id', pc.post_id, 'slug', pc.slug, 'sort_order', p.sort_order, 'name', pc.name) )
FROM post_content pc
LEFT JOIN post p ON (pc.post_id = p.post_id)
LEFT JOIN post_to_taxonomy_item ptt ON (ptt.taxonomy_item_id = @taxonomy_item_id AND ptt.post_id = p.post_id)
WHERE ptt.taxonomy_item_id = @taxonomy_item_id
AND p.type = 'post'
ORDER by p.sort_order
@SQL_LIMIT(0, 5)
) AS post
FROM taxonomy_item AS categories
INNER JOIN taxonomy_to_site t2s ON (categories.taxonomy_item_id = t2s.taxonomy_item_id AND t2s.site_id = 6)
INNER JOIN taxonomy_item_content tc ON (categories.taxonomy_item_id = tc.taxonomy_item_id AND tc.language_id = 1)
INNER JOIN taxonomy t ON (categories.taxonomy_id = t.taxonomy_id)
LEFT JOIN post_to_taxonomy_item pt ON (categories.taxonomy_item_id = pt.taxonomy_item_id)
WHERE
tc.language_id = 1 AND t2s.site_id = 6
AND t.type = 'categories'
ORDER BY categories.parent_id, categories.sort_order, categories.taxonomy_item_id
LIMIT 333 OFFSET 0
- SELECT DISTINCT categories.taxonomy_item_id, @taxonomy_item_id := categories.taxonomy_item_id, categories.*, tc.*, tc.content as content, categories.taxonomy_item_id as array_key
,(SELECT JSON_ARRAYAGG( JSON_OBJECT('post_id', pc.post_id, 'slug', pc.slug, 'sort_order', p.sort_order, 'name', pc.name) )
FROM post_content pc
LEFT JOIN post p ON (pc.post_id = p.post_id)
LEFT JOIN post_to_taxonomy_item ptt ON (ptt.taxonomy_item_id = @taxonomy_item_id AND ptt.post_id = p.post_id)
WHERE ptt.taxonomy_item_id = @taxonomy_item_id
AND p.type = :post_type
ORDER by p.sort_order
@SQL_LIMIT(:posts_start, :posts_limit)
) AS post
FROM taxonomy_item AS categories
INNER JOIN taxonomy_to_site t2s ON (categories.taxonomy_item_id = t2s.taxonomy_item_id AND t2s.site_id = :site_id)
INNER JOIN taxonomy_item_content tc ON (categories.taxonomy_item_id = tc.taxonomy_item_id AND tc.language_id = :language_id)
INNER JOIN taxonomy t ON (categories.taxonomy_id = t.taxonomy_id)
LEFT JOIN post_to_taxonomy_item pt ON (categories.taxonomy_item_id = pt.taxonomy_item_id)
WHERE
tc.language_id = :language_id AND t2s.site_id = :site_id
AND t.type = :type
ORDER BY categories.parent_id, categories.sort_order, categories.taxonomy_item_id
LIMIT :limit OFFSET :start
- Array
(
[0] => Array
(
[file] => /home/www/vvveb/www/storage/model/app/categorysql.mysqli.php
[line] => 722
[function] => execute
[class] => Vvveb\System\Db\Mysqli
[type] => ->
)
)
[file] => /home/www/vvveb/www/system/db/mysqli.php
[line_no] => 626
[line] => throw new \Exception($message, $e->getCode()); // <==
[lines] => Array
(
[0] => list($parameters, $types) = $this->paramsToQmark($sql, $params, $paramTypes);
[1] =>
[2] => try {
[3] => $stmt = self::$link->prepare($sql);
[4] => } catch (\mysqli_sql_exception $e) {
[5] => $message = $e->getMessage() . "\n" . $this->debugSql($origSql, $params, $paramTypes) . "\n - " . $origSql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
[6] =>
[7] => throw new \Exception($message, $e->getCode()); // <==
[8] => }
[9] =>
[10] => if ($stmt && ! empty($types)) {
[11] => array_unshift($parameters, $types);
[12] =>
[13] => //hack for php 7.x bind_param "expected to be a reference, value given" stupid warning
)
[trace] => #0 /home/www/vvveb/www/storage/model/app/categorysql.mysqli.php(722): Vvveb\System\Db\Mysqli->execute()
#1 /home/www/vvveb/www/app/component/categories/pages.php(53): Vvveb\Sql\CategorySQL->getCategoriesPages()
#2 /home/www/vvveb/www/system/component/component.php(432): Vvveb\Component\Categories\Pages->results()
#3 /home/www/vvveb/www/system/component/component.php(386): Vvveb\System\Component\Component->regenerateUncachedComponents()
#4 /home/www/vvveb/www/system/component/component.php(266): Vvveb\System\Component\Component->processComponentCache()
#5 /home/www/vvveb/www/system/component/component.php(136): Vvveb\System\Component\Component->loadComponents()
#6 /home/www/vvveb/www/system/component/component.php(97): Vvveb\System\Component\Component->__construct()
#7 /home/www/vvveb/www/system/core/view.php(520): Vvveb\System\Component\Component::getInstance()
#8 /home/www/vvveb/www/system/core/response.php(166): Vvveb\System\Core\View->render()
#9 /home/www/vvveb/www/system/core/frontcontroller.php(272): Vvveb\System\Core\Response->output()
#10 /home/www/vvveb/www/system/core/frontcontroller.php(322): Vvveb\System\Core\FrontController::call()
#11 /home/www/vvveb/www/system/core/frontcontroller.php(396): Vvveb\System\Core\FrontController::redirect()
#12 /home/www/vvveb/www/system/core/startup.php(521): Vvveb\System\Core\FrontController::dispatch()
#13 /home/www/vvveb/www/index.php(150): Vvveb\System\Core\start()
#14 /home/www/vvveb/www/public/index.php(28): include('...')
#15 {main}
[code] => Array
(
[0] =>
[2] => /**
[3] => * Vvveb
[4] => *
[5] => * Copyright (C) 2022 Ziadin Givan
[6] => *
[7] => * This program is free software: you can redistribute it and/or modify
[8] => * it under the terms of the GNU Affero General Public License as
[9] => * published by the Free Software Foundation, either version 3 of the
[10] => * License, or (at your option) any later version.
[11] => *
[12] => * This program is distributed in the hope that it will be useful,
[13] => * but WITHOUT ANY WARRANTY; without even the implied warranty of
[14] => * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
[15] => * GNU Affero General Public License for more details.
[16] => *
[17] => * You should have received a copy of the GNU Affero General Public License
[18] => * along with this program. If not, see .
[19] => *
[20] => */
[21] =>
[22] => namespace Vvveb\System\Db;
[23] =>
[24] => use Vvveb\System\Event;
[25] =>
[26] => /**
[27] => * Polyfill for mysqli_result when native fetch_all is missing on mysqli_stmt.
[28] => *
[29] => * Wraps a prepared statement and provides fetch_all, fetch_assoc, and fetchRow
[30] => * methods by binding result columns and iterating through the statement.
[31] => */
[32] => class mysqli_result {
[33] => /** @var \mysqli_stmt The underlying prepared statement. */
[34] => private \mysqli_stmt $stmt;
[35] =>
[36] => /** @var ?\mysqli_result Metadata for the statement result. */
[37] => private ?\mysqli_result $meta = null;
[38] =>
[39] => /** @var array row. */
[40] => private $row = [];
[41] =>
[42] => /**
[43] => * @param \mysqli_stmt $stmt The prepared statement to wrap.
[44] => */
[45] => public function __construct(\mysqli_stmt $stmt) {
[46] => $this->stmt = $stmt;
[47] =>
[48] => $this->stmt->store_result();
[49] => $meta = $this->stmt->result_metadata();
[50] => $params = [];
[51] =>
[52] => if ($meta) {
[53] => $this->row = [];
[54] => /*
[55] => for ($i = 0;$i < $meta->field_count; $i++) {
[56] => $field = $meta->fetch_field_direct($i);
[57] => $name = $field->name;
[58] => $this->row[$name] = [];
[59] => $params[] = &$this->row[$name];
[60] => }
[61] => */
[62] =>
[63] => while ($field = $meta->fetch_field()) {
[64] => $name = preg_replace('/[^a-zA-Z0-9_-]+/', '', $field->name);
[65] => $this->row[$name] = [];
[66] => $params[] = &$this->row[$name];
[67] => }
[68] =>
[69] => call_user_func_array([$this->stmt, 'bind_result'], $params);
[70] => }
[71] => }
[72] =>
[73] => /**
[74] => * Fetch all rows as an associative array (delegates to fetch_assoc).
[75] => *
[76] => * @return array> All result rows.
[77] => */
[78] => public function fetch_all(): array {
[79] => return $this->fetch_assoc();
[80] => }
[81] =>
[82] => /**
[83] => * Fetch all rows as an associative array (delegates to fetch_assoc).
[84] => *
[85] => * @param int $resulttype Ignored — always returns associative.
[86] => *
[87] => * @return array>|null All result rows.
[88] => */
[89] => public function fetch_array(int $resulttype): ?array {
[90] => if ($this->stmt->fetch()) {
[91] => //dereference
[92] => $row = [];
[93] =>
[94] => foreach ($this->row as $key => $value) {
[95] => if ($resulttype == MYSQLI_ASSOC) {
[96] => $row[$key] = $value;
[97] => } else {
[98] => $row[] = $value;
[99] => }
[100] => }
[101] =>
[102] => return $row;
[103] => }
[104] =>
[105] => //$this->stmt->close();
[106] => return null;
[107] => }
[108] =>
[109] => /**
[110] => * Fetch all rows as an associative array by binding result columns.
[111] => *
[112] => * @return array> All result rows.
[113] => */
[114] => public function fetch_assoc(): array {
[115] => $result = [];
[116] => while ($return = $this->stmt->fetch()) {
[117] => $c = [];
[118] =>
[119] => $row = $this->row;
[120] => foreach ($row as $key => $val) {
[121] => $c[$key] = $val;
[122] => }
[123] => $result[] = $c;
[124] => }
[125] =>
[126] => return $result;
[127] => }
[128] =>
[129] => /**
[130] => * Stub for interface compatibility — does nothing.
[131] => *
[132] => * @return void
[133] => */
[134] => public function fetch_field(): void {
[135] => }
[136] =>
[137] => /**
[138] => * Stub for interface compatibility — does nothing.
[139] => *
[140] => * @return void
[141] => */
[142] => public function fetch_fields(): void {
[143] => }
[144] =>
[145] => /**
[146] => * Fetch all rows as an associative array (alias for fetch_assoc).
[147] => *
[148] => * @return array> All result rows.
[149] => */
[150] => public function fetchRow(): array {
[151] => if ($this->stmt->fetch()) {
[152] => //dereference
[153] => $row = [];
[154] =>
[155] => foreach ($this->row as $key => $value) {
[156] => $row[$key] = $value;
[157] => }
[158] =>
[159] => return $row;
[160] => }
[161] =>
[162] => //$this->stmt->close();
[163] => return [];
[164] => }
[165] => }
[166] =>
[167] => /**
[168] => * MySQL database driver using the mysqli extension.
[169] => *
[170] => * Provides connection management, query execution with prepared statements,
[171] => * result fetching, escaping, and schema introspection for MySQL/MariaDB.
[172] => */
[173] => class Mysqli extends DBDriver {
[174] => /** @var ?\MySQLi The shared database connection (singleton per process). */
[175] => private static ?\MySQLi $link = null;
[176] =>
[177] => /** @var ?\mysqli_stmt The current prepared statement, if any. */
[178] => private ?\mysqli_stmt $stmt = null;
[179] =>
[180] => /** @var int Number of rows affected by the last query. */
[181] => public int $affected_rows = 0;
[182] =>
[183] => /** @var ?int The auto-increment ID generated by the last INSERT. */
[184] => public ?int $insert_id = null;
[185] =>
[186] => /** @var string Identifier quote character for MySQL. */
[187] => public string $quote = '`';
[188] =>
[189] => /** @var string Table name prefix. */
[190] => public string $prefix = ''; //'vv_';
[191] =>
[192] => /**
[193] => * Get the MySQL server version as an integer.
[194] => *
[195] => * @return int|null The server version (e.g. 80031) or null if not connected.
[196] => */
[197] => public static function version(): ?int {
[198] => if (self :: $link) {
[199] => return mysqli_get_server_version(self :: $link);
[200] => }
[201] =>
[202] => return null;
[203] => }
[204] =>
[205] => /**
[206] => * Get the MySQL server version string.
[207] => *
[208] => * @return string|null The version string (e.g. '8.0.31') or null if not connected.
[209] => */
[210] => public static function info(): ?string {
[211] => if (self :: $link) {
[212] => return mysqli_get_server_info(self :: $link);
[213] => }
[214] =>
[215] => return null;
[216] => }
[217] =>
[218] => /**
[219] => * Get the error message from the last MySQL operation.
[220] => *
[221] => * @return string|null The error message, or null if not connected.
[222] => */
[223] => public function error(): ?string {
[224] => if (self :: $link) {
[225] => return self :: $link->error;
[226] => }
[227] =>
[228] => return null;
[229] => }
[230] =>
[231] => /**
[232] => * Get the error code from the last MySQL operation.
[233] => *
[234] => * @return int|null The error code, or null if not connected.
[235] => */
[236] => public function errorCode(): ?int {
[237] => if (self :: $link) {
[238] => return self :: $link->errno;
[239] => }
[240] =>
[241] => return null;
[242] => }
[243] =>
[244] => /**
[245] => * Wrap a mysqli_stmt in a mysqli_result polyfill for fetch_all support.
[246] => *
[247] => * @param \mysqli_stmt $stmt The prepared statement.
[248] => *
[249] => * @return mysqli_result The polyfilled result object.
[250] => */
[251] => public function get_result(\mysqli_stmt $stmt): mysqli_result {
[252] => $result = new mysqli_result($stmt);
[253] =>
[254] => return $result;
[255] => }
[256] =>
[257] => /**
[258] => * @param string $host Database host.
[259] => * @param string $dbname Database name.
[260] => * @param string $user Database user.
[261] => * @param string $pass Database password.
[262] => * @param int|null $port Database port.
[263] => * @param string $prefix Table name prefix.
[264] => */
[265] => public function __construct(string $host = DB_HOST, string $dbname = DB_NAME, string $user = DB_USER, string $pass = DB_PASS, ?int $port = DB_PORT, string $prefix = DB_PREFIX) {
[266] => }
[267] =>
[268] => /**
[269] => * Connect to the MySQL database.
[270] => *
[271] => * Uses a singleton pattern — returns the existing connection if already open.
[272] => * Throws an exception with a sanitised error message on failure.
[273] => *
[274] => * @param string $host Database host.
[275] => * @param string $dbname Database name.
[276] => * @param string $user Database user.
[277] => * @param string $pass Database password.
[278] => * @param int|null $port Database port.
[279] => * @param string $prefix Table name prefix.
[280] => *
[281] => * @return ?\MySQLi The database connection, or null on failure.
[282] => *
[283] => * @throws \Exception On connection failure.
[284] => */
[285] => public function connect(string $host = DB_HOST, string $dbname = DB_NAME, string $user = DB_USER, string $pass = DB_PASS, ?int $port = DB_PORT, string $prefix = DB_PREFIX): ?\MySQLi {
[286] => //connect to database
[287] => if (self :: $link) {
[288] => return self :: $link;
[289] => }
[290] => $this->prefix = $prefix;
[291] =>
[292] => \mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
[293] =>
[294] => try {
[295] => self :: $link = new \Mysqli($host, $user, $pass, $dbname, $port);
[296] => } catch (\mysqli_sql_exception $e) {
[297] => $errorMessage = str_replace($pass,'*****', $e->getMessage());
[298] =>
[299] => throw new \Exception($errorMessage, $e->getCode());
[300] => }
[301] =>
[302] => // check if a connection established
[303] => if (\mysqli_connect_errno()) {
[304] => $error = mysqli_connect_error();
[305] => $errorMessage = str_replace($pass,'*****', $error);
[306] =>
[307] => throw new \Exception($errorMessage, mysqli_connect_errno());
[308] => }
[309] =>
[310] => if ((! self :: $link instanceof \MySQLi)) {
[311] => throw new \Exception('Mysqli not an object', mysqli_connect_errno());
[312] => }
[313] =>
[314] => //sync database time
[315] => //$this->execute('SET `time_zone` = :zone', ['zone' => $this->escape(date('P'))]);
[316] =>
[317] => return self :: $link;
[318] => }
[319] =>
[320] => /**
[321] => * Get all columns for a table used for sanitizing input.
[322] => *
[323] => * @param string $tableName The table name to introspect.
[324] => * @param bool $comment Whether to include the column comment field.
[325] => *
[326] => * @return array|false
[327] => * Associative array keyed by column name, or false on failure.
[328] => */
[329] => public function getColumnsMeta(string $tableName, bool $comment = false): array|false {
[330] => $sql =
[331] => 'SELECT COLUMN_NAME as name, COLUMN_DEFAULT as d, IS_NULLABLE as n, DATA_TYPE as t, EXTRA as e, CHARACTER_MAXIMUM_LENGTH as l'
[332] => . ($comment ? ', COLUMN_COMMENT as c' : '') .
[333] => ' FROM `INFORMATION_SCHEMA`.`COLUMNS`
[334] => WHERE `TABLE_SCHEMA`= "' . DB_NAME . '"
[335] => AND `TABLE_NAME`="' . $tableName . '"';
[336] =>
[337] => if ($result = $this->query($sql)) {
[338] => //$columns = $result->fetch_all(MYSQLI_ASSOC);
[339] => $columns = [];
[340] =>
[341] => while ($row = $result->fetch_assoc()) {
[342] => $columns[$row['name']] = $row;
[343] => }
[344] =>
[345] => /* free result set */
[346] => $result->close();
[347] =>
[348] => return $columns;
[349] => }
[350] =>
[351] => return false;
[352] => }
[353] =>
[354] => /**
[355] => * Get all table names in the database.
[356] => *
[357] => * @param string $db The database name.
[358] => *
[359] => * @return array|false List of table names, or false on failure.
[360] => */
[361] => public function getTableNames(string $db = DB_NAME): array|false {
[362] => $sql ="SELECT table_name as name
[363] => FROM information_schema.tables
[364] => WHERE table_schema = '$db' ORDER BY table_name";
[365] =>
[366] => if ($result = $this->query($sql)) {
[367] => //$names = $result->fetch_all(MYSQLI_ASSOC);
[368] => $names = [];
[369] =>
[370] => while ($row = $result->fetch_assoc()) {
[371] => $names[] = $row['name'];
[372] => }
[373] =>
[374] => /* free result set */
[375] => $result->close();
[376] =>
[377] => return $names;
[378] => }
[379] =>
[380] => return false;
[381] => }
[382] =>
[383] => /**
[384] => * Select a different database on the current connection.
[385] => *
[386] => * @param string $db The database name.
[387] => *
[388] => * @return bool True on success, false on failure.
[389] => */
[390] => public function select_db(string $db): bool {
[391] => return self :: $link->select_db($db);
[392] => }
[393] =>
[394] => /**
[395] => * Execute a raw SQL query.
[396] => *
[397] => * Automatically connects if not connected. Updates affected_rows and insert_id.
[398] => *
[399] => * @param string $sql The SQL query to execute.
[400] => *
[401] => * @return bool|\mysqli_result Query result or true for success, false on failure.
[402] => *
[403] => * @throws \Exception On query failure.
[404] => */
[405] => public function query(string $sql): bool|\mysqli_result {
[406] => if (! self :: $link) {
[407] => $this->connect();
[408] => }
[409] =>
[410] => if (LOG_SQL_QUERIES) {
[411] => error_log($sql);
[412] => }
[413] =>
[414] => try {
[415] => $result = self :: $link->query($sql);
[416] => } catch (\mysqli_sql_exception $e) {
[417] => $message = $e->getMessage() . "\n" . $sql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
[418] =>
[419] => throw new \Exception($message, $e->getCode());
[420] => }
[421] =>
[422] => if ($result) {
[423] => $this->affected_rows = self :: $link->affected_rows;
[424] => $this->insert_id = self :: $link->insert_id;
[425] => }
[426] =>
[427] => return $result;
[428] => }
[429] =>
[430] => /**
[431] => * Execute multiple SQL statements in a single call.
[432] => *
[433] => * @param string $sql The SQL statements to execute (semicolon-separated).
[434] => *
[435] => * @return bool True on success, false on failure.
[436] => */
[437] => public function multi_query(string $sql): bool {
[438] => if (! self :: $link) {
[439] => $this->connect();
[440] => }
[441] =>
[442] => $result = self :: $link->multi_query($sql);
[443] =>
[444] => if ($result) {
[445] => $this->affected_rows = self :: $link->affected_rows;
[446] => $this->insert_id = self :: $link->insert_id;
[447] => }
[448] =>
[449] => return $result;
[450] => }
[451] =>
[452] => /**
[453] => * Escape a value for safe inclusion in a SQL query.
[454] => *
[455] => * Strings are escaped with real_escape_string. Null values become 'null'.
[456] => * All other types are returned as-is.
[457] => *
[458] => * @param mixed $string The value to escape.
[459] => *
[460] => * @return mixed The escaped value.
[461] => */
[462] => public function escape(mixed $string): mixed {
[463] => if (is_string($string)) {
[464] => return self :: $link->real_escape_string($string);
[465] => }
[466] =>
[467] => if (is_null($string)) {
[468] => return 'null';
[469] => }
[470] =>
[471] => return $string;
[472] => }
[473] =>
[474] => /**
[475] => * Escape a string value for use in a SQL literal.
[476] => *
[477] => * @param string $string The string to escape.
[478] => *
[479] => * @return string The escaped string.
[480] => */
[481] => public function escapeLiteral(string $string): string {
[482] => return $this->escape($string);
[483] => }
[484] =>
[485] => /**
[486] => * Generate a MySQL-compatible LIMIT clause.
[487] => *
[488] => * @param int|string $start Offset (0-based).
[489] => * @param int|string $limit Maximum number of rows.
[490] => *
[491] => * @return string The LIMIT clause (e.g. 'LIMIT 0, 10').
[492] => */
[493] => public function sqlLimit(int|string $start, int|string $limit): string {
[494] => return "LIMIT $start, $limit";
[495] => }
[496] =>
[497] => /**
[498] => * Fetch a single row from a statement or result set.
[499] => *
[500] => * @param \mysqli_stmt|\mysqli_result $stmt The statement or result to fetch from.
[501] => *
[502] => * @return array The fetched row, or empty array if no rows.
[503] => */
[504] => public function fetchArray(\mysqli_stmt|\mysqli_result $stmt): array {
[505] => $data = [];
[506] => $statement = ($stmt instanceof \mysqli_stmt);
[507] =>
[508] => if ($statement) {
[509] => $this->store_result();
[510] => $result = $stmt->get_result();
[511] => } else {
[512] => $result = $stmt;
[513] => }
[514] =>
[515] => if ($result) {
[516] => $result = $result->fetch_array(MYSQLI_ASSOC);
[517] =>
[518] => if ($result) {
[519] => $data = $result;
[520] =>
[521] => if ($statement && $stmt->more_results()) {
[522] => $stmt->next_result();
[523] => }
[524] => }
[525] => }
[526] =>
[527] => return $data;
[528] => }
[529] =>
[530] => /**
[531] => * Fetch all rows from a statement or result set.
[532] => *
[533] => * @param \mysqli_stmt|\mysqli_result $stmt The statement or result to fetch from.
[534] => *
[535] => * @return array> All result rows.
[536] => */
[537] => public function fetchAll(\mysqli_stmt|\mysqli_result $stmt): array {
[538] => $data = [];
[539] => $statement = ($stmt instanceof \mysqli_stmt);
[540] =>
[541] => if ($statement) {
[542] => $this->store_result();
[543] => $result = $stmt->get_result();
[544] => } else {
[545] => $result = $stmt;
[546] => }
[547] =>
[548] => if ($result) {
[549] => $data = $result->fetch_all(MYSQLI_ASSOC);
[550] => }
[551] =>
[552] => return $data;
[553] => }
[554] =>
[555] => /**
[556] => * Store the result of the last query in the client-side buffer.
[557] => *
[558] => * @return ?\mysqli_result The stored result, or null on failure.
[559] => */
[560] => public function store_result(): \mysqli_result|false {
[561] => return self :: $link->store_result();
[562] => }
[563] =>
[564] => /**
[565] => * Check if the current statement has more result sets.
[566] => *
[567] => * @return bool True if more results are available.
[568] => */
[569] => public function more_results(): bool {
[570] => return self :: $link->more_results();
[571] => }
[572] =>
[573] => /**
[574] => * Move to the next result set in a multi-query.
[575] => *
[576] => * @return bool True on success, false on failure.
[577] => */
[578] => public function next_result(): bool {
[579] => return self :: $link->next_result();
[580] => }
[581] =>
[582] => /**
[583] => * Close the database connection.
[584] => *
[585] => * @return ?bool True on success, null if not connected.
[586] => */
[587] => public function close(): ?bool {
[588] => if ((self :: $link instanceof \MySQLi)/* && self :: $link->ping()*/) {
[589] => return self :: $link->close();
[590] => }
[591] =>
[592] => return null;
[593] => }
[594] =>
[595] => /**
[596] => * Prepare and execute a parameterized query.
[597] => *
[598] => * Fires an Event::trigger hook before execution. Converts named parameters
[599] => * to positional placeholders, binds parameters, and executes the statement.
[600] => * Updates affected_rows and insert_id on success.
[601] => *
[602] => * @param string $sql The SQL query with :named parameters.
[603] => * @param array $params Named parameter values.
[604] => * @param array $paramTypes Optional explicit type overrides.
[605] => *
[606] => * @return ?\mysqli_stmt The executed statement, or null on failure.
[607] => *
[608] => * @throws \Exception On prepare or execute failure.
[609] => */
[610] => public function execute(string $sql, array $params = [], array $paramTypes = []): ?\mysqli_stmt {
[611] => list($sql, $params) = Event::trigger(__CLASS__,__FUNCTION__, $sql, $params);
[612] => //save orig sql for debugging info
[613] => $origSql = $sql;
[614] =>
[615] => if (! self :: $link) {
[616] => $this->connect();
[617] => }
[618] =>
[619] => list($parameters, $types) = $this->paramsToQmark($sql, $params, $paramTypes);
[620] =>
[621] => try {
[622] => $stmt = self::$link->prepare($sql);
[623] => } catch (\mysqli_sql_exception $e) {
[624] => $message = $e->getMessage() . "\n" . $this->debugSql($origSql, $params, $paramTypes) . "\n - " . $origSql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
[625] =>
[626] => throw new \Exception($message, $e->getCode()); // <==
[627] => }
[628] =>
[629] => if ($stmt && ! empty($types)) {
[630] => array_unshift($parameters, $types);
[631] =>
[632] => //hack for php 7.x bind_param "expected to be a reference, value given" stupid warning
[633] => $referenceArray = [];
[634] =>
[635] => foreach ($parameters as $key => $value) {
[636] => $referenceArray[$key] = &$parameters[$key];
[637] => }
[638] =>
[639] => @call_user_func_array([$stmt, 'bind_param'], $referenceArray);
[640] => }
[641] =>
[642] => if (LOG_SQL_QUERIES) {
[643] => error_log($this->debugSql($origSql, $params, $paramTypes));
[644] => }
[645] =>
[646] => if ($stmt) {
[647] => try {
[648] => if ($stmt->execute()) {
[649] => $this->affected_rows = self :: $link->affected_rows;
[650] => $this->insert_id = self :: $link->insert_id;
[651] =>
[652] => return $stmt;
[653] => } else {
[654] => error_log(print_r($stmt, 1));
[655] => error_log($this->debugSql($sql, $params, $paramTypes));
[656] => }
[657] => } catch (\mysqli_sql_exception $e) {
[658] => $message = $e->getMessage() . "\n" . $origSql . "\n" . $this->debugSql($origSql, $params, $paramTypes) . "\n" . print_r($parameters, 1) . $types . "\n" . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
[659] =>
[660] => throw new \Exception($message, $e->getCode());
[661] => }
[662] => } else {
[663] => error_log(print_r($stmt, 1));
[664] => error_log($this->debugSql($origSql, $params, $paramTypes));
[665] => }
[666] =>
[667] => return $stmt;
[668] => }
[669] =>
[670] => /**
[671] => * Bind a parameter value to the current prepared statement.
[672] => *
[673] => * @param string|int $param The parameter index or name.
[674] => * @param mixed $value The value to bind.
[675] => * @param int|null $type The PDO type constant, or null for auto-detection.
[676] => *
[677] => * @return void
[678] => */
[679] => public function bind(string|int $param, mixed $value, ?int $type = null): void {
[680] => // TODO: bindValue() is PDO-only; store bindings for later use if needed
[681] => }
[682] => }
)
)