dbuser = $dbuser; $this->dbpass = $dbpass; $this->dbname = $dbname; $this->connect(); } function __destruct() { $this->rollback(); $this->close(); } function getInfos(): array { return [$this->dbname, $this->dbuser, $this->dbpass]; } private function connect() { if ($this->conn === null) { $conn = oci_pconnect($this->dbuser, $this->dbpass, $this->dbname, static::NLS_CHARSET); if (!$conn) throw self::query_exception(null, "unable to connect"); $this->conn = $conn; } } function setDateFormat($datetime=true) { if ($datetime) $this->nls_date_format = self::NLS_DATE_FORMAT_DATETIME; else $this->nls_date_format = self::NLS_DATE_FORMAT_DATEONLY; } function setSortcomp($nlsSort="BINARY_CI", $nlsComp="LINGUISTIC") { $this->nlsSort = $nlsSort; $this->nlsComp = $nlsComp; } private function initSession() { if (!$this->inSession) { # format des dates $req = oci_parse($this->conn, "alter session set nls_date_format = '$this->nlsDateFormat'"); if (!$req) throw self::query_exception($req, "error setting nls_date_format"); oci_execute($req); oci_free_statement($req); # tri et comparaison if ($this->nlsSort) { $req = oci_parse($this->conn, "alter session set nls_sort = $this->nlsSort"); if (!$req) throw self::query_exception($req, "error setting nls_sort"); oci_execute($req); oci_free_statement($req); } if ($this->nlsComp) { $req = oci_parse($this->conn, "alter session set nls_comp = $this->nlsComp"); if (!$req) throw self::query_exception($req, "error setting nls_comp"); oci_execute($req); oci_free_statement($req); } $this->inSession = true; } } function beginTransaction(): void { $this->inTransaction = true; } function commit(): void { if ($this->inTransaction) { $this->inTransaction = false; $this->inSession = false; if ($this->conn && oci_commit($this->conn) === false) { throw self::query_exception(null, "error on commit"); } } } function rollback(): void { if ($this->inTransaction) { $this->inTransaction = false; $this->inSession = false; if ($this->conn && oci_rollback($this->conn) === false) { throw self::query_exception(null, "error on rollback"); } } } private function bindParam($stmt, string $name, &$values): void { if (A::is_assoc($values)) { # tableau associatif: on peut spécifier la longueur et le type de la valeur if (is_array($values["value"])) { # tableau de valeurs $max_array_length = A::get($values, "max_array_length", null); if ($max_array_length === null) $max_array_length = count($values["value"]); $max_item_length = A::get($values, "max_item_length", -1); $type = A::get($values, "type", SQLT_CHR); $r = oci_bind_array_by_name($stmt, $name, $values["value"], $max_array_length, $max_item_length, $type); } else { # valeur simple $max_length = A::get($values, "max_length", -1); $type = A::get($values, "type", SQLT_CHR); $r = oci_bind_by_name($stmt, $name, $values["value"], $max_length, $type); } } elseif (is_array($values)) { # valeur tableau, binder sur chaque élément individuellement foreach ($values as $i => &$value) { if (A::is_assoc($value)) { # tableau associatif: on peut spécifier la longueur et le type de la valeur $max_length = A::get($values, "max_length", -1); $type = A::get($values, "type", SQLT_CHR); $r = oci_bind_by_name($stmt, "${name}_${i}", $value["value"], $max_length, $type); } else { $r = oci_bind_by_name($stmt, "${name}_${i}", $value); } if ($r === false) break; }; unset($value); } else { # valeur simple $r = oci_bind_by_name($stmt, $name, $values); } if ($r === false) { oci_free_statement($stmt); throw self::query_exception($stmt, "error binding with $name"); } } function _execute0(string $sql, ?array &$bindings, array $params): array { self::fix_sql_with_seq_bindings($sql, $bindings); $this->connect(); $this->initSession(); if ($params["transaction"]) $this->beginTransaction(); $stmt = oci_parse($this->conn, $sql); if ($stmt === false) throw self::query_exception($stmt, "preparation error"); if ($bindings !== null) { foreach ($bindings as $name => &$values) { # IMPORTANT: il faut binder sur l'adresse de $values $this->bindParam($stmt, $name, $values); }; unset($values); } if ($this->inTransaction) $mode = OCI_NO_AUTO_COMMIT; else $mode = OCI_COMMIT_ON_SUCCESS; $r = oci_execute($stmt, $mode); if ($r === false) throw self::query_exception($stmt, "execute error"); $r = []; if ($params["num_rows"]) { $numRows = oci_num_rows($stmt); if ($numRows === false) throw self::query_exception(null, "error getting num_rows"); $r["num_rows"] = $numRows; } if ($params["last_insert_id"]) { $r["insert_id"] = null; } if ($params["stmt"]) { $r["stmt"] = $stmt; } else { oci_free_statement($stmt); } return $r; } function _prepareLogger(string $sql, ?array $bindings): array { $queryLogger = $this->queryLogger; $actualQuery = null; $traceSql = config::k("trace_sql", false); if ($traceSql || $queryLogger !== null) { $actualQuery = OracleQuery::build_actual_query($sql, $bindings); } if ($traceSql) debug::log("SQL TRACE --", $actualQuery); return [$queryLogger, $actualQuery]; } function _execute1(string $sql, ?array &$bindings, array $params): array { [$queryLogger, $actualQuery] = $this->_prepareLogger($sql, $bindings); $r = $this->_execute0($sql, $bindings, $params); if ($queryLogger !== null) $queryLogger->logQuery($actualQuery); return $r; } function _execute(string $sql, ?array &$bindings=null, ?array $params=null): array { md::ensure_schema($params, self::EXECUTE_PARAMS_SCHEMA); return $this->_execute1($sql, $bindings, $params); } function _fetchAll(string $sql, ?array &$bindings=null): array { ["stmt" => $stmt ] = $this->_execute1($sql, $bindings, self::EXECUTE_PARAMS_DQL); try { $rows = []; $num_rows = oci_fetch_all($stmt, $rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW + OCI_ASSOC); if ($num_rows === false) throw self::query_exception(null, "fetch error"); return $rows; } finally { oci_free_statement($stmt); } } function _fetchFirst(string $sql, ?array &$bindings=null): ?array { ["stmt" => $stmt ] = $this->_execute1($sql, $bindings, self::EXECUTE_PARAMS_DQL); $row = oci_fetch_assoc($stmt); oci_free_statement($stmt); return $row !== false? $row: null; } function _update(string $sql, ?array &$bindings=null): int { ["num_rows" => $numRows ] = $this->_execute1($sql, $bindings, self::EXECUTE_PARAMS_DML_UPDATE); return $numRows; } function query(?string $sql=null, ?array $filter=null, ?IRowIncarnation $incarnation=null): IQuery { return new OracleQuery($this, $sql, $filter, $incarnation); } function close(): void { if ($this->conn !== null) { $r = oci_close($this->conn); $this->conn = null; if ($r === false) { throw self::query_exception(null, "unable to close"); } } } }