<?php
/**
* Clase de Abstracción de bases de datos Sencilla y de fácil uso.
* @package class.littledb.php
* @author Cody Roodaka <roodakazo@hotmail.com>
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
* @version $Revision: 0.2.9
* @access public
* @see https://github.com/roodaka/littledb
*/
class LittleDB
{
// Instancia de LittleDB
protected static $instance = null;
/**
* Recurso MySQL
* @var resource
*/
public $conn = null;
/**
* Arreglo con los datos de conexión al servidor MySQL
* @var array
*/
protected $data = array(
'host' => null,
'port' => null,
'user' => null,
'pass' => null,
'name' => null
);
/**
* Función de registro
* @var array|string
*/
protected $logger = null;
/**
* Función de manejo de errores
* @var array|string
*/
protected $errors = null;
/**
* Prefijo de la base de datos
* @var string
*/
public $prefix = '';
/**
* Cantidad de consultas realizadas
* @var integer
*/
public $count = 0;
/**
* Constructor de la clase
* @param string $host Url o DNS del Servidor MySQL
* @param string $user Usuario del servidor
* @param string &pass Contraseña del servidor
* @param string $db Nombre de la base de datos
* @param mixed $logger Función para el registro de datos
* @param mixed $errors Función para el manejo de errores
* @return nothing
* @author Cody Roodaka <roodakazo@gmail.com>
*/
private function __construct($host, $user, $pass, $db, $prefix = null, $logger = null, $errors = null)
{
$this->data['host'] = $host;
$this->data['user'] = $user;
$this->data['pass'] = $pass;
$this->data['name'] = $db;
$this->prefix = $prefix;
$this->errors = $errors;
$this->logger = $logger;
// Conectamos a la base de datos.
$this->connect();
} // private function __construct();
/**
* Desconectar & Destruir la clase
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __destruct()
{
return $this->disconect();
} // public function __destruct();
/**
* No permitimos la clonación de LittleDB
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __clone()
{
return $this->error('', 'La clonación de este objeto LittleDB no está permitida.');
} // public function __clone();
/**
* Si por alguna razón alguien necesita esta clase como un texto, retornamos
* texto.
* @return string
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __toString()
{
$cfg = array();
foreach($this->data as $field => $value)
{
$cfg[] = $field.'='.$value;
}
return __CLASS__.'['.implode(';', $cfg).']';
} // public function __toString();
/**
* Si se invoca esta clase será con el fin de generar una consulta a la base
* de datos a modo de Alias a la función $this->select.
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __invoke($table, $fields, $condition = null)
{
return $this->select($query, $values, $ret);
} // public function __invoke();
/**
* Retornamos la configuración de la clase cuando ésta es serializada.
* @return array
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __sleep()
{
return array(
'data' => $this->data,
'logger' => $this->logger,
'errors' => $this->errors,
'prefix' => $this->prefix
);
} // public function __sleep();
/**
* Conectamos a la base de datos cuando ésta es deserializada
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __wakeup()
{
$this->connect();
} // public function __wakeup();
/**
* Obtenemos una instancia de la clase, la primera vez pasamos los parámetros
* para conectar.
* @return object $instance Instancia de la base de datos.
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
*/
public static function get_instance($host = null, $user = null, $pass = null, $db = null, $prefix = null, $logger = null, $errors = null)
{
if(self::$instance === null)
{
self::$instance = new LittleDB($host, $user, $pass, $db, $prefix, $logger, $errors);
}
return self::$instance;
} // public static function get_instance();
/**
* Conectar al servidor MySQL
* @author Cody Roodaka <roodakazo@gmail.com>
*/
private function connect()
{
$this->conn = mysqli_connect($this->data['host'], $this->data['user'], $this->data['pass'], $this->data['name']) or $this->error('', 'No se pudo conectar al servidor MySQL');
} // private function connect();
/**
* Desconectar del servidor MySQL
* @author Cody Roodaka <roodakazo@gmail.com>
*/
private function disconect()
{
return ($this->conn !== null) ? mysqli_close($this->conn) : true;
} // private function connect();
/**
* Procesar una consulta compleja en la base de datos
* @param string $cons Consulta SQL
* @param miexed $values Arreglo con los valores a reemplazar por Parse_vars
* @param boolean $ret retornar Array de datos o no.
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function query($cons, $values = null, $ret = false)
{
$query = ($values != null) ? $this->parse_vars($cons, $values) : $cons;
if($ret == true)
{
$res = $this->_query($query);
if($res !== false)
{
$return = $res->fetch_assoc();
$res->free();
}
else
{
$return = false;
$this->error($query);
}
}
else
{
$return = new Query($query, $this->errors, $this->conn);
++$this->count;
}
return $return;
} // public function query();
/**
* Seleccionamos campos de una tabla
* @param string $table Nombre de la tabla objetivo
* @param array|string $fields
* @param array $condition Condicionante para la selección
* @param array|integer $limit Límite de filas
* @author Cody Roodaka <roodakazo@gmail.com>
* @return array
*/
public function select($table, $fields, $condition = null, $limit = null)
{
if($condition !== null)
{
$where = array();
foreach($condition as $key => $value)
{
$where[] = $key.' = '.$this->parse_input($value);
}
$string = ' WHERE '.implode(' && ', $where);
}
else
{
$string = '';
}
$limits = ($limit !== null) ? ((is_array($limit)) ? $limit[0].', '.$limit[1] : '0, '.$limit) : '0, 1';
$cons = 'SELECT '.(is_array($fields) ? implode(', ', $fields) : $fields).' FROM '.$this->data['name'].$this->prefix.$table.$string.' LIMIT '.$limits);
$query = $this->_query($cons)
if(!$query || $query == false)
{
$this->error($cons);
return false;
}
else
{
if($limits !== null)
{
return new Query($query, $this->errors, $this->conn);
++$this->count;
}
else
{
return $query->fetch_assoc();
}
}
} // public function select();
/**
* Insertar Datos en una tabla
* @param string $table Nombre de la tabla
* @param array $data Arreglo asosiativo con los datos
* @return integer|boolean Número de filas afectadas o False.
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function insert($table, $data)
{
if(is_array($data) === true)
{
$cons = 'INSERT INTO '.$this->data['name'].'.'.$this->prefix.$table.' ( '.implode(', ', array_keys($data)).' ) VALUES ( '.$this->parse_input($data).' )';
$query = $this->_query($cons);
// Seteamos el resultado,
return (!$query || $query == false) ? $this->error($cons) : $this->conn->insert_id;
}
else { return false; }
} // public function insert();
/**
* Borrar una fila
* @param string $table nombre de la tabla
* @param array $cond Condicionantes
* @return integer|boolean Número de filas afectadas o False.
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function delete($table, $cond = array())
{
if(is_array($cond) == true)
{
$where = array();
foreach($cond as $key => $value)
{
$where[] = $key.' = '.$this->parse_input($value);
}
$cons = 'DELETE FROM '.$this->data['name'].'.'.$this->prefix.$table.' WHERE '.implode(' && ', $where);
$query = $this->_query($cons);
return (!$query || $query == false) ? $this->error($cons) : $this->conn->affected_rows;
} else { return false; }
} // public function delete();
/**
* Actualizar una fila
* @param string $table nombre de la tabla
* @param array $array Arreglo asosiativo con los datos
* @param array $cond Condicionantes
* @return integer|boolean Número de filas afectadas o False.
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function update($table, $array = array(), $cond = array())
{
if(is_array($cond) == true && $table != null)
{
$fiel = array();
foreach($array as $field => $value)
{
$fiel[] = $field.' = '.$this->parse_input($value);
}
$wher = array();
foreach($cond as $field => $value)
{
$wher[] = $field.' = '.$this->parse_input($value);
}
$cons = 'UPDATE '.$this->data['name'].'.'.$this->prefix.$table.' SET '.implode(', ', $fiel).' WHERE '.implode(' && ', $wher);
$query = $this->_query($cons);
return (!$query || $query == false) ? $this->error($cons) : $this->conn->affected_rows;
} else { return false; }
} // public function update();
/**
* Ejecutamos una consulta
* @param string $query Cosulta SQL
* @return resource
* @author Cody Roodaka <roodakazo@gmail.com>
*/
private function _query($query)
{
++$this->count;
if($this->logger !== null)
{
call_user_func_array($this->logger, array($query));
}
return mysqli_query($this->conn, $query);
} // private function _query();
/**
* Retornamos un error grave del servidor
* @param string $query Consulta que origina el error
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function error($query, $error = null)
{
if($this->errors !== null)
{
call_user_func_array($this->errors, array($query, (($error !== null) ? $error : mysqli_error($this->conn)) ));
}
} // function error();
/**
* Funcion encargada de realizar el parseo de la consulta SQL agregando las
* variables de forma segura mediante la validacion de los datos.
* En la consulta se reemplazan los ? por la variable en $params
* manteniendo el orden correspondiente.
* @param string $q Consulta SQL
* @param array $params Arreglo con los parametros a insertar.
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
*/
protected function parse_vars($q, $params)
{
// Si no es un arreglo lo convertimos
if(!is_array($params)) { $params = array($params); }
//Validamos que tengamos igual numero de parametros que de los necesarios.
if(count($params) != preg_match_all("/\?/", $q, $aux))
{
throw new Exception('No coinciden la cantidad de parametros necesarios con los provistos en '.$q);
return $q;
}
//Reemplazamos las etiquetas.
foreach($params as $param)
{
$q = preg_replace("/\?/", $this->parse_input($param), $q, 1);
}
return $q;
} // protected function parse_vars();
/**
* Función que se encarga de determinar el tipo de datos para ver si debe
* aplicar la prevención de inyecciones SQL, si debe usar comillas o si es
* un literal ( funcion SQL ).
* @param mixed $objet Objeto a analizar.
* @return string Cadena segura.
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
*/
protected function parse_input($object)
{
if(is_object($object)) //Es un objeto?
{
return (string) $object;
}
elseif(is_int($object)) // Es un número?
{
return (int) $object;
}
elseif($object === NULL) // Es nulo?
{
return 'NULL';
}
elseif(is_array($object)) //Es un arreglo?
{
return implode(', ', array_map(array($this, 'parse_input'), $object));
}
else //Suponemos una cadena
{
return '\''.mysqli_real_escape_string($this->conn, $object).'\'';
}
} // protected function parse_input();
} // class LittleDB();
/**
* Clase para manipular resultados de consultas MySQL, esta clase no es
* comunmente accesible y es creada por LittleDB
* @author Cody Roodaka <roodakazo@gmail.com>
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
* @access private
*/
class Query //Implements Iterator
{
/**
* Recurso MySQL
* @var resource
*/
private $data = false;
/**
* Resultado de la consulta
* @var array
*/
private $result = array();
/**
* Posición
* @var integer
*/
private $position = 0;
/**
* Nro de filas
* @var integer
*/
public $rows = 0;
/**
* Inicializar los datos
* @param string $query Consulta SQL
* @param string $eh Nombre de la función que manipula los errores
* @param resource $conn Recurso de conección SQL
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function __construct($query, $eh, $conn)
{
$cons = mysqli_query($conn, $query);
if(is_object($cons))
{
$this->data = $cons;
$this->position = 0;
$this->rows = $this->data->num_rows;
return true;
}
else
{
if($eh !== null)
{
call_user_func_array($eh, array($query, mysqli_error($conn)));
}
}
} // function __construct();
/**
* Cuando destruimos el objeto limpiamos la consulta.
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
*/
public function __destruct()
{
return $this->free();
} // public function __destruct();
/**
* Limpiamos la consulta
* @author Ignacio Daniel Rostagno <ignaciorostagno@vijona.com.ar>
*/
private function free()
{
return (is_resource($this->data)) ? $this->data->free() : true;
} // private function free();
/**
* Devolvemos el array con los datos de la consulta
* @param string $field Campo objetivo.
* @param string $default Valor a retornar si el campo no existe o está vacío.
* @return array|string Todos los campos o sólo uno
* @author Cody Roodaka <roodakazo@gmail.com>
*/
public function fetch($field = null, $default = null)
{
$this->result = $this->data->fetch_assoc();
if($field !== null) // Pedimos un campo en especial
{
return (isset($this->result[$field])) ?$this->result[$field] : $default;
}
else
{
return $this->result;
}
} // public function fetch();
} // class Query
/**
* Excepción exclusiva de LittleDB.
* @author Cody Roodaka <roodakazo@gmail.com>
* @access private
*/
class LDB_Exception Extends Exception { } // class LDB_Exception();
/**
* LDB: Función improvisada para el manejo de errores
* @param string $query Consulta que origina el error
* @param string $error Mensaje de error provisto por el servidor MySQL
* @return nothing
* @author Cody Roodaka <roodakazo@gmail.com>
*/
function ldb_error($query, $error)
{
exit('<h2>Data Base Error</h2>'.(($query !== '') ?'<span>Error en la consulta <i>'.$query.'</i></br>' : '').'<b>'.$error.'</b></span>');
// throw new LDB_Exception('<h2>Data Base Error</h2>'.(($query !== '') ?'<span>Error en la consulta <i>'.$query.'</i></br>' : '').'<b>'.$error.'</b></span>');
} // function ldb_error();
/**
* LDB: Función Improvisada para el registro de datos
* @param string $data Datos a registrar
* @return nothing
* @author Cody Roodaka <roodakazo@gmail.com>
*/
function ldb_log($data)
{
// do something.
} // function ldb_log(); |