<?php
/**
* @class sqliteConnection
* @brief SQLite Database Driver
*
* See the {@link dbLayer} documentation for common methods.
*
* @package Clearbricks
* @subpackage DBLayer
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
/* @cond ONCE */
if (class_exists('dbLayer')) {
/** @endcond */
class sqliteConnection extends dbLayer implements i_dbLayer
{
protected $__driver = 'sqlite';
protected $__syntax = 'sqlite';
protected $utf8_unicode_ci = null;
protected $vacuum = false;
public function db_connect($host, $user, $password, $database)
{
if (!class_exists('PDO') || !in_array('sqlite', PDO::getAvailableDrivers())) {
throw new Exception('PDO SQLite class is not available');
}
$link = new PDO('sqlite:' . $database);
$this->db_post_connect($link, $database);
return $link;
}
public function db_pconnect($host, $user, $password, $database)
{
if (!class_exists('PDO') || !in_array('sqlite', PDO::getAvailableDrivers())) {
throw new Exception('PDO SQLite class is not available');
}
$link = new PDO('sqlite:' . $database, null, null, [PDO::ATTR_PERSISTENT => true]);
$this->db_post_connect($link, $database);
return $link;
}
private function db_post_connect($handle, $database)
{
if ($handle instanceof PDO) {
$this->db_exec($handle, 'PRAGMA short_column_names = 1');
$this->db_exec($handle, 'PRAGMA encoding = "UTF-8"');
$handle->sqliteCreateFunction('now', [$this, 'now'], 0);
if (class_exists('Collator') && method_exists($handle, 'sqliteCreateCollation')) {
$this->utf8_unicode_ci = new Collator('root');
if (!$handle->sqliteCreateCollation('utf8_unicode_ci', [$this->utf8_unicode_ci, 'compare'])) {
$this->utf8_unicode_ci = null;
}
}
}
}
public function db_close($handle)
{
if ($handle instanceof PDO) {
if ($this->vacuum) {
$this->db_exec($handle, 'VACUUM');
}
$handle = null;
$this->__link = null;
}
}
public function db_version($handle)
{
if ($handle instanceof PDO) {
return $handle->getAttribute(PDO::ATTR_SERVER_VERSION);
}
return '';
}
# There is no other way than get all selected data in a staticRecord
public function select($sql)
{
$result = $this->db_query($this->__link, $sql);
$this->__last_result = &$result;
$info = [];
$info['con'] = &$this;
$info['cols'] = $this->db_num_fields($result);
$info['info'] = [];
for ($i = 0; $i < $info['cols']; $i++) {
$info['info']['name'][] = $this->db_field_name($result, $i);
$info['info']['type'][] = $this->db_field_type($result, $i);
}
$data = [];
while ($r = $result->fetch(PDO::FETCH_ASSOC)) { // @phpstan-ignore-line
$R = [];
foreach ($r as $k => $v) {
$k = preg_replace('/^(.*)\./', '', $k);
$R[$k] = $v;
$R[] = &$R[$k];
}
$data[] = $R;
}
$info['rows'] = count($data);
$result->closeCursor(); // @phpstan-ignore-line
return new staticRecord($data, $info);
}
public function db_query($handle, $query)
{
if ($handle instanceof PDO) {
$res = $handle->query($query);
if ($res === false) {
$e = new Exception($this->db_last_error($handle));
throw $e;
}
return $res;
}
return null;
}
public function db_exec($handle, $query)
{
return $this->db_query($handle, $query);
}
public function db_num_fields($res)
{
if ($res instanceof PDOStatement) {
return $res->columnCount();
}
return 0;
}
public function db_num_rows($res)
{
return 0;
}
public function db_field_name($res, $position)
{
if ($res instanceof PDOStatement) {
$m = $res->getColumnMeta($position);
return preg_replace('/^.+\./', '', $m['name']); # we said short_column_names = 1
}
return '';
}
public function db_field_type($res, $position)
{
if ($res instanceof PDOStatement) {
$m = $res->getColumnMeta($position);
switch ($m['pdo_type']) {
case PDO::PARAM_BOOL:
return 'boolean';
case PDO::PARAM_NULL:
return 'null';
case PDO::PARAM_INT:
return 'integer';
default:
return 'varchar';
}
}
return '';
}
public function db_fetch_assoc($res)
{
return false;
}
public function db_result_seek($res, $row)
{
return false;
}
public function db_changes($handle, $res)
{
if ($res instanceof PDOStatement) {
return $res->rowCount();
}
return 0;
}
public function db_last_error($handle)
{
if ($handle instanceof PDO) {
$err = $handle->errorInfo();
return $err[2] . ' (' . $err[1] . ')';
}
return false;
}
public function db_escape_string($str, $handle = null)
{
if ($handle instanceof PDO) {
return trim($handle->quote($str), "'");
}
return $str;
}
public function escapeSystem($str)
{
return "'" . $this->escape($str) . "'";
}
public function begin()
{
if ($this->__link instanceof PDO) {
$this->__link->beginTransaction();
}
}
public function commit()
{
if ($this->__link instanceof PDO) {
$this->__link->commit();
}
}
public function rollback()
{
if ($this->__link instanceof PDO) {
$this->__link->rollBack();
}
}
public function db_write_lock($table)
{
$this->execute('BEGIN EXCLUSIVE TRANSACTION');
}
public function db_unlock()
{
$this->execute('END');
}
public function vacuum($table)
{
$this->vacuum = true;
}
public function dateFormat($field, $pattern)
{
return "strftime('" . $this->escape($pattern) . "'," . $field . ') ';
}
public function orderBy()
{
$default = [
'order' => '',
'collate' => false,
];
foreach (func_get_args() as $v) {
if (is_string($v)) {
$res[] = $v;
} elseif (is_array($v) && !empty($v['field'])) {
$v = array_merge($default, $v);
$v['order'] = (strtoupper($v['order']) == 'DESC' ? 'DESC' : '');
if ($v['collate']) {
if ($this->utf8_unicode_ci instanceof Collator) {
$res[] = $v['field'] . ' COLLATE utf8_unicode_ci ' . $v['order'];
} else {
$res[] = 'LOWER(' . $v['field'] . ') ' . $v['order'];
}
} else {
$res[] = $v['field'] . ' ' . $v['order'];
}
}
}
return empty($res) ? '' : ' ORDER BY ' . implode(',', $res) . ' ';
}
public function lexFields()
{
$fmt = $this->utf8_unicode_ci instanceof Collator ? '%s COLLATE utf8_unicode_ci' : 'LOWER(%s)';
foreach (func_get_args() as $v) {
if (is_string($v)) {
$res[] = sprintf($fmt, $v);
} elseif (is_array($v)) {
$res = array_map(fn ($i) => sprintf($fmt, $i), $v);
}
}
return empty($res) ? '' : implode(',', $res);
}
# Internal SQLite function that adds NOW() SQL function.
public function now()
{
return date('Y-m-d H:i:s');
}
}
/* @cond ONCE */
}
/* @endcond */