<?php
# Copyright (C) 2009 José Manuel Carnero <hide@address.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
* postgresql -> PostgreSQL
*
* PHP 4
*/
if(!defined('CLASSDDBB_DIR')) define('CLASSDDBB_DIR',dirname(__FILE__));
/**
* Selector de tipo de base de datos con la que conectar.
*
* @param $motor Motor de base de datos
* @return object
* @access public
*/
function &ddbb_sel($motor = 'mysql', $servidor = 'localhost', $usuario = 'root', $password = '', $ddbb = 'test'){
if($motor == 'mysql' && class_exists('mysqli')) $motor = 'mysqli';
$class = 'ddbb_'.$motor;
include_once(CLASSDDBB_DIR.'/class.'.$class.'.inc'); //si no existe el fichero a incluir no se devuelve error, se controla a continuacion
if(!class_exists($class)){
die('No se puede usar el tipo de base de datos -> '.$class."\n");
return(false);
}
$obj = new $class($servidor, $usuario, $password, $ddbb);
return($obj);
}
class ddbb{
//variables privadas
var $bConectado; //indica si se ha conseguido conectar o no a la base de datos
//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 $sCharset; //codificacion en que se lanzaran las querys; a vacio usara la que tenga la base de datos; valores: utf-8,
//var $sCollation; //collation en que se lanzaran las querys; vacio usara la que tenga la base de datos; util para evitar problemas en las comparaciones de cadenas de distintas tablas
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 $sSubTipoQuery; //subtipo de query que se esta realizando, para consultas alter, create o drop: table, database
var $sPrefijo; //prefijo para las tablas
var $sPrefijoId; //identificador de prefijo para las tablas que sera sustituido con $this->sPrefijo, por defecto '_p_'; no usar ni % ni $
var $aTablas; //array con las tablas implicadas en la consulta
var $aCampos; //array con las campos de la consulta, en la forma: ('nombre' => nombre, 'tipo' => tipo, 'long' => longitud, 'flags' => flags) (para cada campo de los resultados)
var $iTotalFilas; //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"
var $aTiempos; //array de tiempos empleado en consultas; asociativo:
/*
Conectar -> tiempo en conectar a servidor,
SeleccionarDDBB -> tiempo en seleccionar base de datos,
$this->sTipoQuery -> tiempo en realizar consulta,
[fila][] -> tiempo para el recuento de resultados, 1 por fila de resultados, todos con respecto a un mismo tiempo inicial
totalLimit -> tiempo para el recuento de resultados en consultas tipo SELECT limitadas
filasTodos -> tiempo para rellenado del array "$aFilaTodos"
*/
var $sTagsPermitidosIns; //cadena de texto con tags html permitidos (en consultas insert/update, en la forma '<p><strong>[...]'; 'todo' -> permite todos los tags (comportamiento por defecto), '' -> (o sin valor) elimina todos los tags
var $sTagsPermitidosSel; //cadena de texto con tags html permitidos (en consultas que devuelvan datos, como select), en la forma '<p><strong>[...]'; 'todo' -> permite todos los tags, '' -> (o sin valor) elimina todos los tags (comportamiento por defecto)
//control de errores
var $sError;
var $aErrorMensajes; //array con los mensajes de error la clase, a fin de que puedan ser traducidos/modificados; las secuencias %s deben dejarse ya que corresponden a variables que seran luego sustituidas, si es necesario poner un % se pondra como %%
/**
* Constructor
* Los parametros por defecto conectan a un servidor local,
* con el usuario administrador (sin clave) y a la base de datos "test"
*
*/
function ddbb($servidor = 'localhost', $usuario = 'root', $password = '', $ddbb = 'test'){
$this->bConectado = false;
//$this->sMotor = 'mysql';
$this->sDDBBServidor = $servidor;
$this->sDDBBUsuario = $usuario;
$this->sDDBBPassword = $password;
$this->sDDBB = $ddbb;
$this->aResultados = array();
$this->aFila = array();
$this->aFilaTodos = array();
$this->sTipoQuery = '';
$this->sSubTipoQuery = '';
$this->sPrefijo = '';
$this->sPrefijoId = '_p_';
$this->aTablas = array();
$this->iTotalFilas = 0;
$this->sUltimaId = false;
$this->aTiempos = array();
$this->sTagsPermitidosIns = 'todos';
$this->sTagsPermitidosSel = '';
$this->sError = false;
$this->aErrorMensajes = array(
'tipoQuery1' => 'Tipo de consulta no soportada: %s'
);
}
/**
* Contador de instancias de la clase;
* se usa al cerrar la conexion a la base de datos,
* para no dejar instancias sin acceso.
*
* @param $inc Incremento
* @return integer
* @access private
*/
function instCount($inc = 1){
if(!isset($iInstCount)){
static $iInstCount = 0;
}
if($inc == 1) $iInstCount++;
if($inc == -1) $iInstCount--;
//echo('$iInstCount --> '.$iInstCount);
return($iInstCount);
}
/**
* Extrae la codificacion de la pagina en la que se esta ejecutando el objeto
* y la convierte a un formato reconocido por la base de datos.
*
* @param $tipo Tipo de dato devuelto
* @return string
* @access protected
*/
function codificacion($tipo = 'charset'){
//extraer codificacion de la pagina actual de las cabeceras enviadas al navegador
if(!isset($this->sCharset)) $this->sCharset = '';
if(!isset($this->sCollation)) $this->sCollation = '';
$aCodificacion = array();
//necesita php>=4.3.0
if(function_exists('headers_list')) $aCodificacion = headers_list();
elseif(function_exists('apache_response_headers')) $aCodificacion = apache_response_headers();
//lista de charset: SHOW CHARACTER SET (SQL en MYSQL)
$codificacion = '';
foreach($aCodificacion as $codificacion){
//if(strpos($codificacion, 'charset=') !== false) $this->sCharset = str_replace('"', '', substr($codificacion, strpos($codificacion, 'charset=')+8));
if(strpos($codificacion, 'charset=') !== false) break;
}
if(strpos(strtolower($codificacion), 'utf') !== false){
if($tipo == 'charset') return('utf8');
elseif($tipo = 'collation') return('utf8_general_ci');
}
elseif(strpos(strtolower($codificacion), 'iso-8859-1') !== false || strpos(strtolower($codificacion), 'iso-8859-15') !== false){
if($tipo == 'charset') return('latin1');
elseif($tipo = 'collation') return('latin1_spanish_ci');
}
return(false);
}
/**
* Tipo de query (primera palabra de la sentencia SQL).
* Sin incluir parentesis y demas (caso de UNION)
* TODO detectar (guardando en array) todas las consultas que se hacen (por ejemplo en UNION) y las tablas utilizadas, categorizando segun sean JOIN ...; devuelve nombres imcompletos de tablas cuando llevan espacios
*
* @param $query Query SQL
* @return boolean
* @access protected
*/
function tipoQuery($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
//TODO ver "mysql_field_table"
$this->aTablas = array();
$aPatron = array();
switch($this->sTipoQuery){
case 'alter':
case 'create': //TODO revisar coincidencias y como se guarda el tipo de create y la tabla o base de datos que se crea
case 'drop':
$this->sSubTipoQuery = strtolower($aQuery[1]);
$aPatron[] = '/'.$this->sTipoQuery.'\W+\w*\W+(?:IF.+EXISTS)*\W*([\w\.]*)/i';
$aPatron[] = '/REFERENCES\W+([\w\.]*)/i';
break;
case 'describe':
case 'explain':
case 'show':
if(strpos(strtolower($query), '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 'lock':
$aPatron[] = '/TABLES\W+([\w\.]*)/i'; //TODO puede cerrar mas de una tabla en la misma instruccion
break;
case 'insert':
if(strpos(strtolower($query), 'into') !== false){
$aPatron[] = '/INTO\W+([\w\.]*)/i';
break;
}
case 'update':
$aPatron[] = '/'.$this->sTipoQuery.'\W+([\w\.]*)/i';
break;
case 'set':
case 'unlock':
case 'use':
break;
default:
$this->sError .= sprintf($this->aErrorMensajes['tipoQuery1'], $this->sTipoQuery)."\n";
return(false);
}
$this->sQuery = $query;
foreach($aPatron as $sPat){
if(preg_match_all($sPat, $query, $regs, PREG_PATTERN_ORDER)){
for($cont=0;$cont<count($regs[0]);$cont++){
$sTemp = '';
if(strpos($regs[1][$cont], '.') === false) $sTemp = str_replace($this->sPrefijoId, $this->sPrefijo, $regs[1][$cont]);
else $sTemp = str_replace($this->sPrefijoId, $this->sPrefijo, substr($regs[1][$cont], strpos($regs[1][$cont], '.')+1));
$this->aTablas[$sTemp] = $sTemp;
$this->sQuery = str_replace($regs[0][$cont], str_replace($regs[1][$cont], str_replace($this->sPrefijoId, $this->sPrefijo, $regs[1][$cont]), $regs[0][$cont]), $this->sQuery); //prefijado de tablas en la consulta
}
$this->aTablas = array_keys($this->aTablas); //elimina entradas duplicadas
}
}
return(true);
}
/**
* Devuelve microtime en segundos
*
* @return float
* @access private
*/
function microtimeSeg(){
list($mseg, $seg) = explode(" ", microtime());
return((float)$mseg + (float)$seg);
}
}
/*
ejemplo de uso:
include_once("./inc/class.ddbb.inc");
$oTest = ddbb_sel('mysql', DB_SERVER, DB_USER, DB_PASSWORD, DB_DATABASE);
//$oTest->conectar(DB_SERVER, DB_USER, DB_PASSWORD, DB_DATABASE); //se puede omitir si se han pasado los parametros de conexion al constructor
$oTest->consulta("SELECT * FROM usuarios");
while($oTest->leeFila()){
echo($oTest->aFila['login']."\n");
}
//var_dump($oTest->aTiempos);
$oTest->desconectar();
*/
?>