Clase para interacción con bases de datos
June 4th, 2008
Esto no es ADOdb (nadie se confunda), ni lo pretende.
La intención era crear una pequeña clase que simplificase la vida a la hora de acceder a bases de datos. Una capa de abstracción para no tener que recordar como se conecta o deja de conectar a tal o cual motor de base de datos, etc.
Por ahora está en desarrollo (posiblemente lo estará siempre), pero es completamente funcional que no libre de errores.
Esta implementado MYSQL y parcialmente POSTGRESQL; es lo que he necesitado hasta la fecha.
# Copyright (C) 2008 jmcb <jmanuel@2mdc.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
//clase para conexion y querys a bases de datos
//de momento solo MYSQL (#TODO# otros motores de DDBB)
//Se marcan con #required# las funciones o pasos necesarios para el uso basico de la clase
/*
* motores soportados ("$this->sMotor"):
* mysql -> MySQL 4, 5
*/
class ddbb{
//variables privadas
var $dConexion; //identificador de enlace
var $bSeleccion; //estado de seleccion de base de datos (true, false)
var $rResultados; //identificador de la consulta
//variables publicas
var $sMotor; //motor de base de datos (mysql, postgresql)
var $sDDBBServidor; //servidor de base de datos; nombre cualificado o ip
var $sDDBBUsuario; //usuario de la base de datos
var $sDDBBPassword; //clave del usuario
var $sDDBB; //base de datos a usar (schema)
var $aFila; //una fila de resultados
var $aFilaTodos; //array con todas las filas de resultados
var $sQuery; //SQL query completa, pasar entre comillas dobles ("); se envia a las funciones como cadena, cada "%s" implica una sustitucion con un valor del array pasado (todos sus elementos deben ser cadenas de caracteres, ver "fConsulta" y "fQuery")
var $sQueryTipoArray; //define que tipo de array devolvera una query: dos (MYSQL_BOTH) -> ambos tipos de array, aso (MYSQL_ASSOC) -> asociativo, num (MYSQL_NUM) -> numerico; por defecto (si no se define explicitamente valor): ambos; es valido asignandole las constantes entre parentesis (naturales de PHP)
var $sTipoQuery; //tipo de query que se esta realizando: select, insert, update, delete, ...
var $aTablas; //array con las tablas implicadas en la consulta
var $iTotalRow; //total de registros encontrados
var $sUltimaId; //ultima id insertada en campos "autoincrement"; false mientras no haya inserciones; devuelve valor incorrecto si el campo id de la tabla es "BIGINT"
//control de errores
var $sError;
/**
* Constructor
* Los parametros por defecto conectan a un servidor de MYSQL local,
* con el usuario administrador (sin clave) y a la base de datos "test"
* (que existe por defecto)
*
*/
function ddbb(){
$this->sMotor = 'mysql';
$this->sDDBBServidor = 'localhost';
$this->sDDBBUsuario = 'root';
$this->sDDBBPassword = '';
$this->sDDBB = 'test';
$this->aResultados = array();
$this->aFila = array();
$this->aFilaTodos = array();
$this->sQueryTipoArray = 'dos';
$this->sTipoQuery = '';
$this->aTablas = array();
$this->iTotalRow = 0;
$this->sUltimaId = false;
$this->sError = false;
}
//contador de instancias de la clase; se usa al cerrar la conexion a la base de datos, para no dejar instancias sin acceso
function fInstCount($inc = 1){
if(!isset($iInstCount)) static $iInstCount = 0;
if($inc == 1) $iInstCount++;
if($inc == -1) $iInstCount--;
//echo('$iInstCount --> '.$iInstCount);
return($iInstCount);
}
//#required#
//conexion al servidor
//
/**
* Conexión al servidor.
* Selecciona automaticamente la base de datos,
* llamar a "fSeleccionarDDBB()" cambiando la propiedad "$this->sDDBB"
* para usar otra base de datos con esta misma conexion.
*
* @param Servidor $servidor Usuario $usuario Clave $clave Schema $db
* @return boleano
* @access public
*/
function fConectar($servidor = false, $usuario = false, $clave = false, $db = false){
ddbb::fInstCount();
if($servidor) $this->sDDBBServidor = $servidor;
if($usuario) $this->sDDBBUsuario = $usuario;
if($clave) $this->sDDBBPassword = $clave;
$this->dConexion = mysql_connect($servidor, $usuario, $clave);
if($this->dConexion === false){
$this->sError .= 'No se puede conectar a la base de datos '.$this->sDDBB.', error:'.mysql_error($this->dConexion).")\n";
return(false);
}
return($this->fSeleccionarDDBB($db));
}
//#required#
//conexion al servidor
function fDesconectar(){
//$bool = mysql_free_result($this->rResultados); //fuerza liberacion de memoria, solo sirve para "SELECT" (devuelve booleano)
if(ddbb::fInstCount(-1) == 0){
$sReturn = mysql_close($this->dConexion);
if(!$this->bSeleccion) $this->sError .= 'No se puede cerrar la conexion a la base de datos '.$this->sDDBB.', error:'.mysql_error($this->dConexion).")\n";
}
else $sReturn = true;
return($sReturn);
}
//eleccion de base de datos
function fSeleccionarDDBB($db = false){
if($db !== false) $this->sDDBB = $db;
$this->bSeleccion = mysql_select_db($this->sDDBB, $this->dConexion);
if(!$this->bSeleccion) $this->sError .= 'No se puede seleccionar la base de datos '.$this->sDDBB.', error:'.mysql_error($this->dConexion).")\n";
return($this->bSeleccion);
}
/*
* tipo de query (primera palabra de la sentencia SQL)
*
* TODO detectar (guardando en array) todas las consultas que se hacen (por ejemplo en UNION) y las tablas utilizadas, categorizando segun sean JOIN ...
*/
function fTipoQuery($query = false){
if($query === false) $query = $this->sQuery;
$aQuery = explode(' ', trim(str_replace(array('(', ')'), '', $query))); //evita que en UNION o similar (empiezan con parentesis) no encuentre el primer SELECT
$this->sTipoQuery = strtolower($aQuery[0]);
//que tablas estan implicadas en la consulta
$this->aTablas = array();
$aPatron = array();
switch($this->sTipoQuery){
case 'describe':
case 'explain':
if(strpos(strtolower($this->sQuery), 'select') === false){
$aPatron[] = '/'.$this->sTipoQuery.'\W+(\w*)/i';
break;
}
case 'select':
$aPatron[] = '/FROM\W+(\w*)/i';
$aPatron[] = '/JOIN\W+(\w*)/i';
break;
case 'delete':
$aPatron[] = '/FROM\W+(\w*)/i';
break;
case 'insert':
if(strpos(strtolower($this->sQuery), 'into') !== false){
$aPatron[] = '/INTO\W+(\w*)/i';
break;
}
case 'update':
$aPatron[] = '/'.$this->sTipoQuery.'\W+(\w*)/i';
break;
default:
//$this->sError .= 'Tipo de consulta no soportada: '.$this->sTipoQuery.' (error-> '.mysql_error($this->dConexion).")\n";
}
foreach($aPatron as $sPat){
if(preg_match_all($sPat, $this->sQuery, $regs, PREG_PATTERN_ORDER)){
foreach($regs[1] as $reg){
$this->aTablas[] = $reg;
}
}
}
}
//construir query
//si ya se ha asignado una query mediante la propiedad "sQuery" y no se desea cambiar NO llamar a esta funcion y llamar al metodo "fConsulta" sin parametros
//"$cantidad" e "$inicial" devuelven un rango de resultados ("LIMIT" en MYSQL, "TOP" en MSSQL, ...)
//ej: $obj->fQuery("SELECT * FROM tabla WHERE campo=%s AND b=%s", array('1', '2'))
//cuando se requiera el comodin SQL "%" escribirlo doble "%%"; ej: $obj->fQuery("SELECT * FROM tabla WHERE campo='%%%s%%'", array('hola')) ->producira la salida: "SELECT * FROM tabla WHERE campo='%hola%'"
function fQuery($query, $pars = array(), $cantidad = 0, $inicial = 0){
//preparar las cadenas para que no den problemas SQL
if(!get_magic_quotes_gpc()){
$pars = array_map('addslashes', $pars);
}
$this->sQuery = vsprintf($query, $pars);
if($cantidad){
switch($this->sMotor){
case 'mysql':
//si "$inicial=0" devuelve "$cantidad" de registros desde el primero
//si se quieren devolver todos los que haya desde un "$inicial=x", dar como "$cantidad" un valor mayor al total de registros que pueda devolver la consulta (un numero al azar suficientemente grande, por ejemplo 123456789123456789123456789)
$this->sQuery .= ' LIMIT '.$inicial.','.$cantidad;
break;
case 'postgresql':
$this->sQuery .= ' LIMIT '.$inicial.' OFFSET '.$cantidad;
break;
}
}
}
//#required#
//consulta a la base de datos, (si "SELECT", necesita de "fLeeFila()" para empezar a devolver resultados)
function fConsulta($query = false, $pars = array(), $cantidad = 0, $inicial = 0){
if($query) $this->fQuery($query, $pars);
$this->fTipoQuery($query); //comprueba el tipo de query
switch($this->sTipoQuery){
case 'select':
case 'describe':
case 'explain':
//#TODO# ver si se puede calcular el total de resultados (con consultas "LIMIT") sin lanzar dos veces la consulta
$this->rResultados = mysql_query($this->sQuery); //array con los resultados sin "LIMIT"
$this->fNumResultados(); //calculo del total de resultados (evitando la posibilidad de "LIMIT")
if($cantidad){
if(is_resource($this->rResultados)) mysql_free_result($this->rResultados); //libera la memoria de los resultados previos, ya que solo fueron necesarios para el recuento total
if($query) $this->fQuery($query, $pars, $cantidad, $inicial);
$this->rResultados = mysql_query($this->sQuery); //array con los resultados
}
break;
case 'delete':
case 'insert':
case 'update':
$this->rResultados = mysql_query($this->sQuery); //array con los resultados
$this->sUltimaId = mysql_insert_id(); //-> devuelve la ultima id insertada
$this->fNumResultados(); //calculo del total de resultados
break;
default:
$this->sError .= 'Tipo de consulta no soportada: -'.$this->sTipoQuery.'- (error-> '.mysql_error($this->dConexion).")\n";
}
if($this->rResultados === false){
$this->sError .= 'La consulta : '.$this->sTipoQuery.' ha generado el error-> '.mysql_error($this->dConexion).")\n";
return(false);
}
else return(true);
}
//calcular el numero de filas devueltas o afectadas por la query
//#TODO# ver informacion de las dos funciones, pueden dar resultados erroneos
function fNumResultados(){
switch($this->sTipoQuery){
case 'select':
case 'describe':
case 'explain':
$this->iTotalRow = mysql_num_rows($this->rResultados);
//var_dump($this->rResultados);
return(true);
break;
case 'delete':
case 'insert':
case 'update':
$this->iTotalRow = mysql_affected_rows($this->dConexion);
if($this->iTotalRow < 1) $this->iTotalRow = false;
return(true);
/*#TODO# If the last query was a DELETE query with no WHERE clause, all of the records will have been deleted from the table but this function will return zero with MySQL versions prior to 4.1.2.*/
break;
default:
$this->sError .= 'No es posible contar el numero de registros, no se reconoce el tipo de query: '.$this->sTipoQuery."\n";
return(false);
}
}
//recolocar puntero en el array de resultados (solo para SELECT)
function fRecPuntero($pos = 0){
if($this->iTotalRow) $bReturn = mysql_data_seek($this->rResultados, $pos); //falla con E_WARNING y devuelve FALSE si el result set esta vacio
else $bReturn = false;
if(!$bReturn) $this->sError .= 'No se ha podido recolocar el puntero debido al error: '.mysql_error($this->dConexion)."\n";
return($bReturn);
}
//#required#
//lee fila de resultados
function fLeeFila(){
//este metodo solo tiene sentido con consultas tipo "SELECT"
if($this->sTipoQuery == 'select' || $this->sTipoQuery == 'describe' || $this->sTipoQuery == 'explain'){
//tipo de array que devolvera la query
switch($this->sQueryTipoArray){
case 'dos':
$this->sQueryTipoArray = MYSQL_BOTH;
case MYSQL_BOTH:
break;
case 'aso':
$this->sQueryTipoArray = MYSQL_ASSOC;
case MYSQL_ASSOC:
break;
case 'num':
$this->sQueryTipoArray = MYSQL_NUM;
case MYSQL_NUM:
break;
default:
$this->sError .= 'El tipo de array que debe devolverse es desconocido'."\n";
}
if($this->aFila = mysql_fetch_array($this->rResultados, $this->sQueryTipoArray)) return(true);
else return(false);
}
else{
$this->sError .= 'Intenta obtener un array de resultados de una consulta no "SELECT"'."\n";
return(false);
}
}
//crea un array con todas las filas de resultados
function fLeeFilasTodos(){
//recoloca el puntero en el inicio para obtener todos los resultados
if($this->iTotalRow > 0) $this->fRecPuntero();
//este metodo solo tiene sentido con consultas tipo "SELECT"
if($this->sTipoQuery == 'select' || $this->sTipoQuery == 'describe' || $this->sTipoQuery == 'explain'){
//tipo de array que devolvera la query
switch($this->sQueryTipoArray){
case 'dos':
$this->sQueryTipoArray = MYSQL_BOTH;
case MYSQL_BOTH:
break;
case 'aso':
$this->sQueryTipoArray = MYSQL_ASSOC;
case MYSQL_ASSOC:
break;
case 'num':
$this->sQueryTipoArray = MYSQL_NUM;
case MYSQL_NUM:
break;
default:
$this->sError .= 'El tipo de array (-'.$this->sQueryTipoArray.'-) que debe devolverse es desconocido'."\n";
}
while($this->aFilaTodos[] = mysql_fetch_array($this->rResultados, $this->sQueryTipoArray)) ;
if($this->aFilaTodos[count($this->aFilaTodos)-1] === false) array_pop($this->aFilaTodos); //el anterior bucle (si todo es correcto) colocara un false en el ultimo elemento, sobra
return(true);
}
else{
$this->sError .= 'Intenta obtener un array de resultados de una consulta no "SELECT"'."\n";
return(false);
}
}
}
/*
ejemplo de uso:
include_once("../inc/class.ddbb.inc");
$oPar_pais = new ddbb();
$oPar_pais->fConectar(DB_SERVER, DB_USER, DB_PASSWORD, DB_DATABASE);
$oPar_pais->fConsulta("SELECT id, fdNombre FROM tbpaises ORDER BY fdNombre");
//este bucle solo para consultas SELECT
while($oPar_pais->fLeeFila()){
echo($oPar_pais->aFila['id']);
}
$oPar_pais->fDesconectar();
*/
Categoría: PHP - MySQL







Deja tu comentario
Tags HTML Permitidos:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed