Sunday 4 October 2009

class_SRR_database_sim :: mi base de datos en PHP

Hacía mucho tiempo que quería compartir con vosotros mi "clase de base de datos" que llevo trabajándome en PHP desde hace ya 3 años largos! La idea nació de la necesidad de almacenar información para una aplicación web de un amigo sin poder usar MySQL ni otro tipo de base de datos tradicional, porque su plan de hosting era el más básico :S

Por fin, después de este tiempo, la base de datos ya está más o menos madura como para compartirla con otros. Lo que más me ha costado es darle propiedades de no "corruptibilidad" por coincidencia de dos threads PHP sobre la misma tabla de datos. Es decir, que ahora por fin la base de datos bloquea el acceso de lectura/escritura cuando algún "thread" está ESCRIBIENDO sobre alguna tabla. De esta forma no puede ocurrir que alguien empiece a leer una tabla cuando alguien la tiene a "medio escribir". Lo cuál aunque parezca muy improbable, empieza a volverse probable en cuanto la base de datos aumenta de tamaño ;)

Queda mucho por hacer, así que la he colgado como proyecto opensource en INDEFERO. Aquí tenéis los enlaces:

[DEMO] http://imasdeweb.com/opensource/php_SRR_database_sim/demo

[HOMEPAGE] http://imasdeweb.indefero.net/p/class-SRR-database-sim

[DOC] http://imasdeweb.indefero.net/p/class-SRR-database-sim/doc

Sois programadores y ya sabéis que cualqueir software siempre es mejorable, pero en fin, si no publico nunca ninguna versión el progreso de la misma irá tan lento como hasta ahora. Y confío que ahora una vez que alguien empieza a descargarla y usarla pueda tener feedbacks constructivos :))


Resúmen de características

  • No requiere en el servidor más que tener PHP instalado
  • Para empezar a usarla solo hay que copiar el directorio de la base de datos en donde más te convenga y tantas "instancias" como quieras.
  • Ocupa muy poco. Estando vacía menos de 50kb.
  • Lleva un archivo admin3.php al que puedes llamar para gestionar tu base de datos de forma básica online (al estilo phpmyadmin pero muuuuuho más humilde, jejejeje).
  • Los registros de cada tabla se almacenan en un archivo de texto plano en el mismo directorio, con lo cuál solo necesitas darle permisos de escritura al directorio ;)
  • La clase ofrece toda una serie de sencillos métodos del tipo: crear/modificar/eliminar tabla y crear/modificar/seleccionar/eliminar registros
  • Los campos de cada tabla pueden contener ilimitados caracteres (olvídate por fin de definir tipo y longitud de campo).
  • Las consultas (o "queries") no se escriben como en SQL en una cadena de texto y un chingo de comillas, sino que son llamadas parametrizadas usando siempre arrays ;)
  • Hacer un backup o migrar a otro hosting es tan fácil y rápido como "copiar y pegar el directorio"
  • Como no es necesario ejecutar un segundo servidor (por ejemplo el de MySQL) sino solamente leer y escribir en archivos de texto plano desde el PHP, la velocidad de respuesta es rapidísima (comparada con MySQL) para tablas con menos de 5.000 registros

Cosas que no tiene

  • índices
  • vínculos entre tablas
  • (un montón de cosas... jejejeje)

En fin, espero que lo pruebes y me digas qué te pareció. Y bueno, si además eres programador... bueno, una ayudita estaría genial!!! Sinceramente, aunque nadie más ponga ni una línea de código en este proyecto, ya estoy muy contento de compartir por primera vez algo realmente mío :)))) le debo tanto a la comunidad de programadores en general... !!!! me siento bien retornando algo de mi parte.

Por cierto, esta clase de base de datos es la que utilizo como pilar de mi CMS, el cuál llevo construyendo y utilizando desde hace también 3-4 años! muy prontito también lo publicaré como opensource, porque considero que a alguien le puede ir bien. Un CMS más, verdad?! jajajaja... pero en fin.. no os lo creeréis, pero ninguno de los CMS que habían en aquel entonces me venían bien! o eran muy complicados para el programador (si necesitaba hacer cambios en la funcionalidad del mismo) o bien tenían un panel de administración bastante complejo para el nivel de mis clientes. Conclusión: acabé programando mi propio CMS. La ventaja: si necesito cualquier cambio lo tengo en un instante. Y si aparece algún bug, enseguida sé de dónde viene ;)

Websites funcionando que usan mi CMS y la susodicha base de datos

Para que veáis que tanto la base de datos como el CMS funcionan a las mil maravillas, aquí os dejo el enlace a unas cuantas de mis páginas que los usan. Y las dos primeras os puedo asegurar que tienen tablas con más de 1.000 registros!

Un saludo!!
SERGI


Actualizaciones posteriores
  • versión 3.2 (3-NOV-2009): se corrigió un error en el método de DELETE_TABLE, y se añadieron unos archivos (test.htm y test.php) para hacer benchmark de acceso simultaneo desde diferentes navegadores (solo para uso de desarrollo de la clase).

  • ...

  • versión 5.0 (17-FEB-2011): nuevas funciones de IMPORTACIÓN/EXPORTACIÓN de tablas (internamente, para cambios de versión de la clase o para migrar datos de un site a otro, etc); nuevas funciones de REGISTRO (o LOG) de escritura y/o lectura! (útil para debugeo de vuestra aplicación web: conocer qué operaciones se han estado realizando y cuando, y cuánto tiempo llevaron. Más adelante le añadiremos funciones de recuperación de datos)

  • para seguir la evolución del proyecto, mejor es visitar el LOG oficial del mismo: en INDEFERO

15 comments:

  1. Excelente clase amigo, gracias por compartir!! ya te estaré mandando algún feedback despues de probarla con un proyecto que me viene de perlas.

    Ya hice una pequeña prueba y funciona excelente.

    Saludos

    ReplyDelete
  2. @arthpix: vaya, ni te imaginas la ilusión que me hace que a alguien le haya servido!!! y tan rápido!

    Me imagino que no hace falta que diga que "no me hago responsable de la pérdida de datos que puedan suceder por errores de código en la clase". Ya me entendéis: yo mismo la utilizo para las webs de mis clientes, y por tanto sus datos están en mis manos! sobre todo los que realizan reservas, ventas, etc... Es una gran responsabilidad el que se pierdan datos!! qué os voy a contar que no sepáis?

    Pero lo que quiero decir es que MÁS QUE EN NINGÚN OTRO CASO, os aseguréis de hacer copias periódicas de seguridad de vuestros datos. Ya sabemos que no hay sistemas infalibles. Pero menos aún un código como el que aquí comparto, que todavía ha tenido una vida demasiado corta como para decir que está "libre de fallos" ! al contrario... cuidadín porque en las primeras versiones tuve mis "sustos".

    El peor de todos los sustos es que se pierdan datos. Es decir, que algún archivo se quede a medio escribir o se sobreescriba o algo así. Tal como explico en la documentación (ver INDEFERO), las últimas versiones ya resolvieron este tipo de incidencias. Pero siempre pueden surgir nuevas, verdad?

    De hecho, podéis estar más o menos tranquilos porque está aguantando muy bien en supertransfers.com y en fundaciomontblanc.org, en los que la base de datos ha tenido que trabajar bastante ;)

    Una cosa que me gustaría sería añadirle en un futuro próximo es un poco de "inteligencia" al sistema: que hiciera una especie de "backup" cuando hubieran reducciones drásticas de información en alguna tabla y avisara por email, para poder restaurar manualmente de ser necesario ;) en fin... más adelante!

    Un saludo!
    Y seguro que te va a ir bien arthpix!!! ;)

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Hola de un post al otro, voy a probar el cms, pero para los que leemos mejor el código que lo creamos podrías decirme como haces para sacar los rows de
    una consulta a una tabla por ejemplo que por cada registro me escriba el id en la url y el nombre en el enlace

    include ("class_SRR_database_sim3.php");
    $database = new class_SRR_database_sim3(true);
    $table_name ="amigos";

    $var = $database->GET_RECORDS_TABLE($table_name,$array_cond);

    while ($rows = mysql_fetch_array($var)){
    ?>
    <a href="index.php?id=<? echo $rows['id']; ?>"><?php echo $rows['title']; ?></a>
    <?php
    }
    ?>

    <?php
    $database->CLOSE();
    ?>

    gracias
    carlos

    ReplyDelete
  5. Hola Carlos, me alegra mucho que te hayas lanzado a probar mi clase de base de datos ;) Voy a ver si puedo ayudarte.

    Te voy a poner el script de cómo deberías hacer esa consulta a la base de datos y esa iteración sobre los resultados, porque realmente no vas bien encaminado ;)

    Simplemente porque la clase no devuelve el típico "objeto" que devuelve una consulta a MySQL (por ejemplo), sino que devuelve simplemente UN ARRAY!!! para mí esta es una de las grandes ventajas de mi clase :D

    Ahí va el código:
    _____________________________________________
    <?php

    // consulta de la base de datos
    include ("class_SRR_database_sim3.php");
    $database = new class_SRR_database_sim3(true);
    $table_name ="amigos";
    $array_cond = array();
    $var = $database->GET_RECORDS_TABLE($table_name,$array_cond);
    $database->CLOSE();

    // iteracion para mostrar resultados en HTML
    $html = "";
    if (count($var)>0){
    foreach ($var as $row){
    $href = "index.php?id=".$row['_id_'];
    $html .= "\n <a href='".$href."'>".$row['title']."</a>";
    }
    }

    // mostrar resultado
    echo $html;
    ?>
    _____________________________________________

    Comentarte, además que la clase de base de datos genera SIEMPRE un campo autonumérico llamado '_id_' que identifica a cada resgistro de una tabla!!! puedes usarlo o no, pero siempre está ahí! y por tanto, facilita mucho la gestión de los registros, sin necesidad de tener siempre que añadir el campo en cada tabla ;)

    Siento haber tardado en ver tu comentario, y espero haberte ayudado ;)

    Un saludo!
    SERGI

    ReplyDelete
  6. @Carlos: gracias a ti, le he dedicado un poquito más de tiempo a estructurar mejor la documentacion en:

    http://imasdeweb.indefero.net/p/class-SRR-database-sim/doc

    Añadí 2 páginas de ejemplos, que espero que no sean las últimas. Es cierto que con buenos ejemplos es cómo más fácilmente se aprende, verdad?!

    Gracias por escribir!
    Ah, y si llegas a utilizar la clase de base de datos en algún site tuyo, dímelo.
    SERGI

    ReplyDelete
  7. Hola gracias por el ejemplo lo estoy probando pero a partir del id (que por cierto con _id_ no me lo coge, he tenido que noner id solo) ya no coge ningún campo o row mas de la tabla, si miro el codigo fuente esta el enlace y falta el title

    Lo último que me comentas si lo he visto y las he creado sin ID.

    Voy probando, la idea era implementar lo de los idiomas con el cms con tablas de textos de cada idioma, soy mas diseñador, lo del php si entiendo el código avanzo ;)

    me miro los ejemplos
    gracias de nuevo

    ReplyDelete
  8. @Carlos: envíame tus archivos en un ZIP si quieres (caos30(at)terra.es), y me dices qué es lo que no funciona y te busco el error ;) O si lo ves más factible, pégame aquí el trozo de código tal como lo tengas...

    ReplyDelete
  9. Es el mismo que me has colgado aquí pero con otra tabla, he creado una con los idiomas (langs) y dos campos lang y language con la abreviación y el nombre completo. me muestra hasta el id pro no el resto de rows ?¿ esto ya no lo hace
    $html .= "\n ".$row['lang']."";

    aquí está el cófdigo modificado y creo que el error esta en $array_cond = array() que le falta algo

    include ("admin/libs/class_SRR_database_sim3.php");
    $database = new class_SRR_database_sim3(true);
    $table_name ="langs";
    $array_cond = array();

    $var = $database->GET_RECORDS_TABLE($table_name,$array_cond);
    $database->CLOSE();

    // iteracion para mostrar resultados en HTML
    $html = "";
    if (count($var)>0){
    foreach ($var as $row){
    $href = "index.php?id=".$row['id'];
    $html .= "\n <a href='".$href."'>".$row['lang']."</a>";
    $html .= "<hr>";
    $html .= "\n lang: ".$row['lang']." corresponde a: ".$row['language'];
    }
    }

    // mostrar resultado
    echo $html;

    ReplyDelete
  10. Upssss... tienes razón Carlos!
    Ya te arreglé el código y lo hice funcionar, te lo comento abajo:
    ______________________________

    <?php

    include ("class_SRR_database_sim3.php");
    $database = new class_SRR_database_sim3(true);
    $table_name ="langs";
    $array_cond = array();
    $html = "";

    $var = $database->GET_RECORDS_TABLE($table_name,$array_cond);
    $database->CLOSE();

    // mostrar datos obtenidos en bruto
    $html .= var_export($var,true); // var_export es una función nativa de PHP

    // iteracion para mostrar resultados en HTML
    if (count($var)>0){
    foreach ($var as $row){
    $href = "index.php?id=".$row['id'];
    $html .= "<hr>";
    $html .= "\n <a href='".$href."'>".$row['campos']['lang']."</a>";
    $html .= "\n lang: ".$row['campos']['lang']." corresponde a: ".$row['campos']['language'];
    }
    }

    // mostrar resultado
    echo $html;

    ?>
    ________________________________________

    Fue un lapsus mío! el array que devuelve la consulta GET_RECORDS_TABLE tiene la siguiente estructura:

    array (

    0 => array ( 0 => 1, 'id' => 1, 'campos' => array ( 'lang' => 'es', 'language' => 'spanish', ), ),

    1 => array ( 0 => 2, 'id' => 2, 'campos' => array ( 'lang' => 'en', 'language' => 'english', ), ),

    )

    Es decir, es un array de ROWS en donde cada ROW es un array de 3 elementos: el 'id' (por duplicado), y un array 'campos' conteniendo lo que realmente nos interesa. Así que para acceder al id:

    $id = $row['id'];

    y para acceder al valor de un campo:

    $lang = $row['campos']['lang'];

    ¿Sabes porqué no caí en cuenta la primera vez? porque para mis aplicaciones me he hecho un pequeño archivo ddbb.php que me hace de "capa intermedia de acceso a la bAse de datos", entre otras cosas por seguridad, para parametrizar las consultas posibles ;)

    Y en este sentido me defino una función del tipo: db_select($table,$where,$index,$order) que me hace varias cosas (me hace la consulta que tú estabas haciendo, y me "reconstruye" después el array obtenido en otro array INDEXADO por el campo que le indico en $index y ordenando los elementos por el campo pasado en $order. Además, el array devuelto por esta función intermedia db_select, realmente contiene ROWS en los que los valores de los campos ya no están en otro array llamado 'campos', como en el array original. Por eso no me di cuenta hasta que no probé el código.

    Ahora sí espero que te vaya bien! ;)
    Un saludo!
    SERGI

    ReplyDelete
  11. Por cierto, te voy a regalar un buen truco de programación en PHP: el uso de la función nativa

    var_export($arr, true);

    Sirve para sacar por pantalla el contenido de un array! y es indispensable en momentos como éste, para saber qué me ha devuelto, por ejemplo, otra función a la que estoy llamando. Una vez que ves cuáles son los datos que se están manejando, entonces es posible arreglar el código ;)

    El único parámetro obligatorio de la función es $arr, que es el nombre del array que quieres mostrar ;)

    También decirte que me hice una función _var_export() que me devuelve "lo mismo que" var_export, pero mínimamente formateado (con sangrados y con color y flechitas) para que puesto en el navegador sea muuuucho más legible que la función nativa de PHP ;)

    la dejo aquí por si alguien la quiere aprovechar (yo no podría trabajar sin ella!!):
    ___________________________________

    function _var_export($arr){
    $html = "\n<div style='margin-left:100px;'>";
    if (is_array($arr)){
    foreach ($arr as $k=>$ele)
    $html .= "\n<div style='float:left;'><b>$k <span style='color:#822;'>-></span> </b></div>"
    ."\n<div style='border:1px #ddd solid;'>"._var_export($ele)."</div>";
    }else{
    $html .= ($arr==NULL)? " ":$arr;
    }
    $html .= "</div>";
    return $html;
    }
    ___________________________________

    ReplyDelete
  12. ahora si funciona ;) te he mandado un zip que he montado en un momento con todo lo que tenia de los dos posts.

    saludos

    ReplyDelete
  13. He creado una demo on-line:

    http://imasdeweb.com/opensource/php_SRR_database_sim/demo

    Es básicamente un acceso al "admin" que trae el paquete de la clase PHP. Es un admin que permite crear/editar/eliminar tablas/registros. Además, he traducido este admin al inglés, y le he dado un lavado de cara en cuanto a estilos (estaba un poco desfasado).

    De esta forma es mucho más fácil probar la base de datos en acción :)

    ReplyDelete
  14. He colgado una versión 4.1 con algunas mejoras en el admin y también en la clase en sí. Entre las mejoras que más me gustan está que en el admin ahora los registros de una tabla muy larga se muestran PAGINADOS (por defecto 50 registros por página).

    Próximamente quiero añadir la posibilidad de ordenar los registros por columnas (ya sabes, haciendo clic en el título de columna), y añadir un dialogo "buscar", que irá bastante bien, jejeje...

    Por cierto, una mejora importante de seguridad: los archivos dónde se guardan los registros de cada tabla ya no tienen extensión ".txt" sino ".php", y la primera línea que contienen es un echo y un return, jejeje, con lo que esos archivos ya no se pueden ver desde el navegador conociendo su ruta, lo cuál resuelve un problema GRAVE de seguridad hasta ahora.

    En fin... pronto más ;)
    Un saludo!
    SERGI

    ReplyDelete
  15. Ya salió la versión 5.0 cargada de interesantes novedades:

    - corrección de bugs menores
    - nuevas funciones de IMPORTACIÓN/EXPORTACIÓN
    - nuevas funciones de REGISTRO (log) de escritura y/o lectura

    Os recomiendo que visitéis la web oficial del LOG del proyecto:

    http://imasdeweb.indefero.net/p/class-SRR-database-sim/page/0-LOG-version/

    Saludos!
    SERGI

    ReplyDelete