Categorí­a: 'PHP - MySQL'

Clase para interacción con bases de datos

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();

*/

Añadir Comentario June 4th, 2008

Decodificando desde ASCII, ISO 8859-1 o UTF-8 (Y 2)

Completando la utilidad anterior que os dimos, os proponemos estas dos pequeñas  funciones que también nos ahorrarán mucho tiempo y código llegado el caso.

Estas dos funciones hace un fetch (de objetos o arrays) sobre una query anterior, pero además nos cambia la codificación de los textos obtenidos de una sóla vez. Consiguiendo así olvidarnos de escribir en la linea del echo un utf8-decode(fila->texto ), htmlenties($fila[’texto’]), etc.

# Hace un fetch de query devolviendo el objeto reformateado
#
#
function mysql_fetch_object_dfw($query){
	
   $objeto=mysql_fetch_object($query);
	
   if($objeto){
	
      foreach(get_object_vars($objeto) AS $ind => $val){
	
         $val=($val==NULL)? '': $val;
         $objeto->$ind	=	decode_for_web($val);
	
      }
	
   }
	
   return $objeto;
	
}
	
# Hace un fetch de query devolviendo el array reformateado
#
#
	
function mysql_fetch_array_dfw($query){
	
   $array_o=mysql_fetch_array($query);
	
   if($array_o){
	
      foreach($array_o AS $ind => $val){
         $val=($val==NULL)? '': $val;
         $array_o[$ind]=decode_for_web($val);
      }
	
   }
	
   return $array_o;
	
}

Añadir Comentario May 9th, 2008

Decodificando desde ASCII, ISO 8859-1 o UTF-8

A todo programador PHP le llega el momento de lidiar con la temible codificación de caracteres al crear una web. Cosas llamadas ISO 8859, UTF, Latin1, JIS. etc… pueden volverte loco si no se ha tenido en cuenta a la hora de crear las BBDD, incluir script de terceros, wordpress, google maps, etc…

Como ayuda hemos tratado de crear una pequeña función para ayudarte a despreocuparte de todo. Ella sólo detecta la codificación del texto pasado y obra en consecuencia, lo cual puede ser muy util si estamos trabajando con caracteres de los que desconocemos su codificación, o peor aún, ¡Tienen codificaciones mezcladas!

<?php
	
# Probamos 3 cadenas de texto con diferentes codificaciones
# la primera con caracteres ASCII (hasta el 128)
# la segunda ISO 8859-1 latin1 (con una eñe)
# y la tercera con el caracter copuright en formato utf8
#
	
//Array donde almacenaremos el resultado de la funcion:
$salida	=	array();
$salida[]	=	decode_for_web("2mdc");
$salida[]	=	decode_for_web("diseño");
$salida[]	=	decode_for_web("WEB ©");
	
//Pintamos la tabla:
echo '<table border="1">';
foreach($salida AS $valor){
	
	echo	'<tr><td>'.$valor[0].'</td>';
	echo	'<td>'.$valor[1].'</td></tr>';
	
}
echo '</table>';
	
# Funcion decodificadora desde ASCII, Latin1 o UTF-8
# Es muy importante conservar el orden del 2do parametro
# en mb_detect_encoding para su correcto funcionamiento.
#
	
function decode_for_web($texto){
	
	$tipo = mb_detect_encoding($texto, "ASCII,UTF-8,
		ISO-8859-1");
	
	switch($tipo){
	
		case "ASCII":
	
		break;
		case "UTF-8":
			$texto	=	utf8_decode($texto);
		break;
		case "ISO-8859-1":
	
		break;
		default:
	
		break;
	
	}
	
	//array con el texto decodificado y su tipo.
	return array($texto,$tipo);
	
}
	
?>

Como se puede ver utilizamos la nativa mb_detect_encoding() la cual es algo rara a la hora de configurar.
Se puede añadir código en las diferentes casos del switch o al final para por ejemplo convertir a entidades HTML antes retornar.

El resultado sería éste:

2mdc ASCII
diseño ISO-8859-1
WEB © UTF-8

Añadir Comentario May 9th, 2008

Búsqueda bruta en MySQL

Aunque no debiera ser así, en ocasiones se nos presenta la situación de buscar cierta palabra o cadena de caracteres en una base de datos sin saber siquiera en que columna o tabla se encuentra. Ante esta necesidad nos surgió hacer esta pequeña herramienta de emergencia, con la que podemos buscar cualquier cadena dentro de cualquier base de datos MySQL, sin la necesidad de crear una consulta SQL con todas las columnas por cada tabla. (Tarea que puede ser desesperante).

El siguiente código tiene 3 pantallas:

La primera de acceso a la base de datos en la que introduciremos la IP donde esté escuchando el servidor (o localhost si estamos en una maquina local), el usuario, la contraseña y la base de datos que queremos examinar.

En la segunda podremos señalar las tablas donde queremos buscar entre todas las que existen e introducir una cadena a buscar.

Y por fin en la tercera pantalla nos aparecerán los registros que tengan coincidencias resaltando la cadena buscada ordenados por tablas.

El código está comentado y podreis adaptarlo, modificarlo y mejorarlo todo lo que querais.

Descargar de 2mdc.Com

Entradas relacionadas: De MySQL a Excel

1 Comentario May 4th, 2007

De MySQL a Excel

La verdad es que el título ya lo dice casi todo.

Desde aquí os podéis descargar una sencilla aplicación que vuelca bases de datos MySQL en archivos Excel.

La aplicación utiliza la compatibilidad XML de las última versiones de Microsoft Office.

El volcar la base de datos en un archivo XML compatible Excel permite, por ejemplo:

  • Guardar diferentes tablas en diferentes hojas
  • Dar formato al archivo Excel resultante mediante hojas de estilo, lo que facilita su legibilidad
  • Hacerle la pelota al jefe presentándole los datos en la única aplicación que jamas ha aprendido a utilizar

El zip de descarga incluye un pequeño archivo de texto con comentarios aclaratorios. Esperamos los vuestros…

Descargar de 2mdc.Com

NOTA: No hemos conseguido convencer a Ricardo (el responsable de haber hecho los scripts) de que cambie el diseño de la página de entrada de la aplicación.
2mdc.com no se hace responsable del mismo :-)

Entradas relacionadas: Búsqueda bruta en MySQL

1 Comentario February 15th, 2006

Modificación de archivos Multiple

Descripción:
Ultimamente nos hemos encontrado con la necesidad de agregar los conocidos ‘headers y footers’ a nuestras webs, por eso desarrollamos esta mini-aplicación open-source con la que conseguiremos modificar recursivamente todos los archivos de la extensión que queramos que esten en un directorio, agregandoles una cabecera y un pie.

Descargar de 2mdc.Com

Como funcionalidad extra también nos brinda la posibilidad de sustituir una cadena de texto o una expresion regular por la cadena de texto que queramos.

La aplicacion esta zipeada. Se compone de:

  • 1html con el formulario donde introducir los parametros
  • 1php que es el script que procesa los archivos
  • 1header y 1footer de ejemplo

Añadir Comentario December 30th, 2005

  Sabías que...
 
Sabías que...

... ya somos más de 1000 millones de internautas.

Hace tan solo 3 años se estimaban en poco más de 600 millones el número de internautas, lo que supone una tasa de incremento anual del 18%.

¿Aún sigue pensando que su empresa puede vivir de espaldas a Internet?

 
 
2mdc.com diseño web     C/ Matias Turrión, 24, 1º, Madrid 28043      Tel: 91 759 00 24       Fax 91 759 15 18     Diseño web