Dado que Wordpress es mucho mejor que Blogspot y tiene más futuro, he mudado la web. Me ha costado un tiempo, así que he ido añadiendo entradas nuevas según traspasaba las anteriores. La dirección es la siguiente:
Constructor sobrecargado
No voy a actualizar este blog, así que por favor seguid las actualizaciones en Wordpress.
jueves, 27 de agosto de 2009
jueves, 6 de agosto de 2009
Abrir reproductor en ventana nueva mediante javascript
Necesitaba que un reproductor basado en jQuery se cargase en un popup y desapareciese de la ventana original para que el usuario pudiese navegar por la página y seguir escuchando las canciones sin interrupción.
Decidí usar variables de sesión. En la página original condicioné que se mostrase el reproductor a si la siguiente variable era falsa:
if Session("ventana_reproductor_abierta") = false then
...
end if
Al reproductor le añadí un enlace y usé jQuery para enlazarle el evento.
//Busco el enlace al que hay que añadirle el evento click.
var botonReproductorVentanaNueva = $('#columnaUno #reproductor-ventana-nueva');
botonReproductorVentanaNueva.click(function(){
abrePopup('carga_reproductor_ventana.asp','Titulo',300,400);
});
function abrePopup(url,titulo,h,w){
newwindow=window.open(url,titulo,'height=' + h + ',width=' + w);
if (window.focus) {newwindow.focus()}
return false;
}
El script ASP 'carga_reproductor_ventana.asp' se asegura de establecer la variable de sesión a verdadero, de manera que al recargarse la página original no se muestre el reproductor:
Session("ventana_reproductor_abierta") = true
Ahora viene la parte difícil. Necesitamos que cuando la ventana abierta se cierre, se devuelva la variable de sesión a falso y se recargue la página que lanzó el popup. Para ello necesitamos una función poco documentada llamada window.onbeforeunload que se lanza cuando uno cierra la ventana. Además necesito llamar a otro script ASP mediante AJAX que se encargará de devolver la variable de sesión al valor adecuado.
//Funciones para el reproductor cargado en un popup.
$(document).ready(function(){
//Controlo el evento que se dispara cuando intenta quitar la página.
window.onbeforeunload = function() {
$.ajax({
contentType: 'application/x-www-form-urlencoded',
type: "GET",
url: "cierra_ventana_reproductor.asp"
});
window.opener.location.reload();
}
});
Como se ve, window.opener permite acceder a la ventana que llamó al popup, y location.reload() recarga la página. El script 'cierra_ventana_reproductor.asp' establece la variable de sesión al valor necesario.
<%
Session("ventana_reproductor_abierta") = false
%>
Decidí usar variables de sesión. En la página original condicioné que se mostrase el reproductor a si la siguiente variable era falsa:
if Session("ventana_reproductor_abierta") = false then
...
end if
Al reproductor le añadí un enlace y usé jQuery para enlazarle el evento.
//Busco el enlace al que hay que añadirle el evento click.
var botonReproductorVentanaNueva = $('#columnaUno #reproductor-ventana-nueva');
botonReproductorVentanaNueva.click(function(){
abrePopup('carga_reproductor_ventana.asp','Titulo',300,400);
});
function abrePopup(url,titulo,h,w){
newwindow=window.open(url,titulo,'height=' + h + ',width=' + w);
if (window.focus) {newwindow.focus()}
return false;
}
El script ASP 'carga_reproductor_ventana.asp' se asegura de establecer la variable de sesión a verdadero, de manera que al recargarse la página original no se muestre el reproductor:
Session("ventana_reproductor_abierta") = true
Ahora viene la parte difícil. Necesitamos que cuando la ventana abierta se cierre, se devuelva la variable de sesión a falso y se recargue la página que lanzó el popup. Para ello necesitamos una función poco documentada llamada window.onbeforeunload que se lanza cuando uno cierra la ventana. Además necesito llamar a otro script ASP mediante AJAX que se encargará de devolver la variable de sesión al valor adecuado.
//Funciones para el reproductor cargado en un popup.
$(document).ready(function(){
//Controlo el evento que se dispara cuando intenta quitar la página.
window.onbeforeunload = function() {
$.ajax({
contentType: 'application/x-www-form-urlencoded',
type: "GET",
url: "cierra_ventana_reproductor.asp"
});
window.opener.location.reload();
}
});
Como se ve, window.opener permite acceder a la ventana que llamó al popup, y location.reload() recarga la página. El script 'cierra_ventana_reproductor.asp' establece la variable de sesión al valor necesario.
<%
Session("ventana_reproductor_abierta") = false
%>
Etiquetas:
ajax,
asp,
javascript,
jQuery,
programación web
miércoles, 5 de agosto de 2009
Función que vacía campos de un formulario que tengan en el contenido un patrón común
$(document).ready(function(){
//Asume que los campos de tipo input text empiezan con "Escriba aquí...",
// y vacía esos campos en focus, siempre que tengan ese valor, y vuelve a escribir ese texto
// en blur si se ha dejado el campo vacío.
var campos=$("form :input[value *= Escriba aqu]");
$.each(campos,function(){
//Por cada campo, compruebo si el contenido contiene la cadena "escriba aqu".
var contenido=$(this).attr("value");
$(this).bind("focus", function(){
if ($(this).attr("value") == contenido){
$(this).attr("value","");
}
});
$(this).bind("blur", function(){
if ($(this).attr("value")==""){
//Ha dejado el campo vacío, así que vuelvo a ponerle de texto lo que había antes de entrar en el foco.
$(this).attr("value",contenido);
}
});
});
});
//Asume que los campos de tipo input text empiezan con "Escriba aquí...",
// y vacía esos campos en focus, siempre que tengan ese valor, y vuelve a escribir ese texto
// en blur si se ha dejado el campo vacío.
var campos=$("form :input[value *= Escriba aqu]");
$.each(campos,function(){
//Por cada campo, compruebo si el contenido contiene la cadena "escriba aqu".
var contenido=$(this).attr("value");
$(this).bind("focus", function(){
if ($(this).attr("value") == contenido){
$(this).attr("value","");
}
});
$(this).bind("blur", function(){
if ($(this).attr("value")==""){
//Ha dejado el campo vacío, así que vuelvo a ponerle de texto lo que había antes de entrar en el foco.
$(this).attr("value",contenido);
}
});
});
});
'player-viral.swf' y otros reproductores flash sin funcionar en ciertos servidores
Anduvimos con un problema de aparente difícil solución al implementar un reproductor programado en flash que cogía archivos de tipo flv. La solución, sobre todo cuando va en un servidor y no en otro, consiste en probar a descargarse directamente el fichero desde donde esté almacenado, en el servidor donde el reproductor no funcione. Si la respuesta del navegador es que no reconoce ese tipo de fichero o que no existe cuando sí debería de reconocerlo, es que no está añadido el MIME Type en IIS.
La siguiente página es una buena manera de empezar a buscar:
Adding .FLV MIME Type in IIS
La siguiente página es una buena manera de empezar a buscar:
Adding .FLV MIME Type in IIS
Usar campos TEXT en consultas con GROUP BY en SQL
En ocasiones no queda más remedio que agrupar consultas, ya que hay que usar funciones de grupo como MIN, MAX, AVG, etc. En este caso necesitábamos un MIN(fecha), además de cargar campos de tipo TEXT que de manera normal no podrían agruparse. Si se hace lo siguiente, la consulta fallará:
SELECT descripcion ,
ficha ,
min(fecha) minfecha
from tabla
GROUP BY
descripcion ,
ficha
ORDER BY min(fecha)
Una consulta normalmente válida de este tipo, siendo los campos 'descripcion' y 'ficha' de tipo TEXT, fallará. Para ello tenemos que engañar convirtiendo los campos de tipo texto en cadenas con longitud limitada mediante la función SUBSTRING. La solución sería la siguiente:
SELECT substring(descripcion,1,500) descripcion ,
substring(ficha,1,500) ficha ,
min(fecha) minfecha
from tabla
GROUP BY
substring(descripcion,1,500) ,
substring(ficha,1,500)
ORDER BY min(fecha)
SELECT descripcion ,
ficha ,
min(fecha) minfecha
from tabla
GROUP BY
descripcion ,
ficha
ORDER BY min(fecha)
Una consulta normalmente válida de este tipo, siendo los campos 'descripcion' y 'ficha' de tipo TEXT, fallará. Para ello tenemos que engañar convirtiendo los campos de tipo texto en cadenas con longitud limitada mediante la función SUBSTRING. La solución sería la siguiente:
SELECT substring(descripcion,1,500) descripcion ,
substring(ficha,1,500) ficha ,
min(fecha) minfecha
from tabla
GROUP BY
substring(descripcion,1,500) ,
substring(ficha,1,500)
ORDER BY min(fecha)
CONCAT en SQL Server
CONCAT es una función propia de MySQL y Oracle que sirve para juntar diferentes campos y cadenas para que se muestren como una en una consulta de SQL. Por desgracia, no puede usarse en SQL Server. Para ello podríamos hacer lo siguiente:
SELECT id,nombre_es + ' (' + tipo + ')' FROM tabla ORDER BY nombre_es asc
SELECT id,nombre_es + ' (' + tipo + ')' FROM tabla ORDER BY nombre_es asc
martes, 4 de agosto de 2009
Error de javascript en IE, sin problemas en Mozilla
Cuando surja un error en IE que impida que algún script funcione, hay que fijarse en las diferencias en interpretación y capacidad para evitar ciertos errores. Nos hemos pasado un buen rato intentando descubrir qué parte del código daba problemas, y resulta que IE no consigue interpretar arrays a cuyo último elemento le sigue una coma. Mozilla lo interpreta sin problemas.
El código era el siguiente:
function almacenaReproduccion(){
var cancion = myPlayList[playItem].filename;
//Los dos otros datos que necesito guardar son el id de la sesión y la fecha y hora
// actual, pero los recojo en el servidor.
$.ajax({
contentType: 'application/x-www-form-urlencoded',
type: "GET",
url: "../web/cuenta_reproduccion.asp",
data: "archivo=" + myPlayList[playItem].filename
});
}
El error se encontraba en la línea siguiente:
data: "archivo=" + myPlayList[playItem].filename
Anteriormente terminaba en una coma, ya que en el pasado le seguía otro parámetro. Como he dicho, Mozilla lo entendía e IE no. Algo a tener en cuenta.
El código era el siguiente:
function almacenaReproduccion(){
var cancion = myPlayList[playItem].filename;
//Los dos otros datos que necesito guardar son el id de la sesión y la fecha y hora
// actual, pero los recojo en el servidor.
$.ajax({
contentType: 'application/x-www-form-urlencoded',
type: "GET",
url: "../web/cuenta_reproduccion.asp",
data: "archivo=" + myPlayList[playItem].filename
});
}
El error se encontraba en la línea siguiente:
data: "archivo=" + myPlayList[playItem].filename
Anteriormente terminaba en una coma, ya que en el pasado le seguía otro parámetro. Como he dicho, Mozilla lo entendía e IE no. Algo a tener en cuenta.
Etiquetas:
IE vs Firefox,
programación web
Guardar una cuenta de reproducciones usando AJAX en ASP
Primero localizamos el botón de play del reproductor y asociamos una función al evento click:
var botonPlay = $('#player_play_archivos');
$(botonPlay).click(function(){
$('#jquery_jplayer').play();
//Necesito los datos de la canción que se está escuchando en este momento.
almacenaReproduccion();
});
La función almacenaReproduccion() coge el nombre del archivo (en el ejemplo, la ruta completa), y la envía por AJAX al servidor.
function almacenaReproduccion(){
var cancion = myPlayList[playItem].filename;
//Los dos otros datos que necesito guardar son el id de la sesión y la fecha y hora
// actual, pero los recojo en el servidor.
$.ajax({
contentType: 'application/x-www-form-urlencoded',
type: "GET",
url: "cuenta_reproduccion.asp",
data: "archivo=" + myPlayList[playItem].filename,
});
}
Como se ve, se llama a un archivo llamado 'cuenta_reproduccion.asp'. En el servidor añado el ID de la sesión actual y la fecha y la hora en la que se ha reproducido el archivo.
dim archivo
archivo = LimpiarRequest(Request("archivo"))
dim intCurrentPos, intFileNamePos
'Cojo el nombre del archivo encontrando la última barra
Do
intCurrentPos = InStr(intCurrentPos + 1, archivo, "/", vbTextCompare)
If intCurrentPos <> 0 then
intFileNamePos = intCurrentPos
else
intFileNamePos = intFileNamePos + 1
Exit Do
End If
Loop
dim FileName
If intFileNamePos <> 1 then
FileName = mid(archivo, intFileNamePos, len(archivo) - intFileNamePos + 1)
End If
'FileNamewExt contiene el nombre del archivo, sin la ruta.
'Necesitamos tanto el id de la sesión y la fecha y hora actuales.
dim idSesion,momentoActual
idSesion = Session.SessionID
momentoActual = now()
'Envío los datos a la consulta que realiza la inserción.
dim resultado
set resultado = setCuentaReproduccion(FileName,idSesion,momentoActual)
'response.write(resultado)
var botonPlay = $('#player_play_archivos');
$(botonPlay).click(function(){
$('#jquery_jplayer').play();
//Necesito los datos de la canción que se está escuchando en este momento.
almacenaReproduccion();
});
La función almacenaReproduccion() coge el nombre del archivo (en el ejemplo, la ruta completa), y la envía por AJAX al servidor.
function almacenaReproduccion(){
var cancion = myPlayList[playItem].filename;
//Los dos otros datos que necesito guardar son el id de la sesión y la fecha y hora
// actual, pero los recojo en el servidor.
$.ajax({
contentType: 'application/x-www-form-urlencoded',
type: "GET",
url: "cuenta_reproduccion.asp",
data: "archivo=" + myPlayList[playItem].filename,
});
}
Como se ve, se llama a un archivo llamado 'cuenta_reproduccion.asp'. En el servidor añado el ID de la sesión actual y la fecha y la hora en la que se ha reproducido el archivo.
dim archivo
archivo = LimpiarRequest(Request("archivo"))
dim intCurrentPos, intFileNamePos
'Cojo el nombre del archivo encontrando la última barra
Do
intCurrentPos = InStr(intCurrentPos + 1, archivo, "/", vbTextCompare)
If intCurrentPos <> 0 then
intFileNamePos = intCurrentPos
else
intFileNamePos = intFileNamePos + 1
Exit Do
End If
Loop
dim FileName
If intFileNamePos <> 1 then
FileName = mid(archivo, intFileNamePos, len(archivo) - intFileNamePos + 1)
End If
'FileNamewExt contiene el nombre del archivo, sin la ruta.
'Necesitamos tanto el id de la sesión y la fecha y hora actuales.
dim idSesion,momentoActual
idSesion = Session.SessionID
momentoActual = now()
'Envío los datos a la consulta que realiza la inserción.
dim resultado
set resultado = setCuentaReproduccion(FileName,idSesion,momentoActual)
'response.write(resultado)
Etiquetas:
ajax,
asp,
jQuery,
programación web
lunes, 3 de agosto de 2009
Problemas con la sesión y objetos desconocidos al trabajar con AJAX en PHP
En un script, llamémosle script1.php, llamaba mediante jQuery a otro script, llamémosle script2.php, intentaba acceder a los datos de la variable $_SESSION. A pesar de establecer el session_start(), y ver mediante el Firebug que podía ver la información, no podía acceder por programación a los atributos de la clase con la que tenía que trabajar. El Firebug la definía de la siguiente manera:
__PHP_Incomplete_Class Object ( [__PHP_Incomplete_Class_Name] ...
Obviamente sugiere que la clase no existe. La solución está en cargar un include o un require con la definición de la clase antes de session_start().
__PHP_Incomplete_Class Object ( [__PHP_Incomplete_Class_Name] ...
Obviamente sugiere que la clase no existe. La solución está en cargar un include o un require con la definición de la clase antes de session_start().
Etiquetas:
ajax,
oop,
php,
programación web
sábado, 1 de agosto de 2009
Explode en jQuery y javascript
Explode es una manera muy útil de sacar información de algún elemento de la web, ya que, por ejemplo, suelo almacenar ids de elementos de la base de datos como parte del nombre de un id o de una clase. Aquí tenemos un ejemplo de explode en jQuery, que sería un split:
var valor=$('#elemento').attr('id').split('filtro-id')[1];
La variable 'valor' contendría ahora 'id'.
var valor=$('#elemento').attr('id').split('filtro-id')[1];
La variable 'valor' contendría ahora 'id'.
Etiquetas:
javascript,
jQuery,
programación web
Parámetros opcionales en las funciones de javascript
Por desgracia, parece ser que no podemos especificar que un parámetro sea igual a un valor como haríamos en otros lenguajes, así que hay que comprobar dentro de la propia función si el parámetro que queremos tener como opcional es undefined, de la siguiente manera:
function Funcion(par1,par2,par3){
//código
if (par3===undefined){
//El par3, opcional, no se ha enviado
}
}
function Funcion(par1,par2,par3){
//código
if (par3===undefined){
//El par3, opcional, no se ha enviado
}
}
Etiquetas:
javascript,
programación web
sábado, 25 de julio de 2009
Encuentra un elemento dentro de un elemento ya seleccionado en jQuery
Digamos que has seleccionado un div llamado #posts que contiene las entradas de un blog. Pero después necesitas modificar elementos concretos que están contenidos dentro de ese div. Se haría de la siguiente manera:
$('#posts')
.find('.firstColumn')
.css('background','red')
.end()
.find('.lastColumn')
.css('background','blue');
$('#posts')
.find('.firstColumn')
.css('background','red')
.end()
.find('.lastColumn')
.css('background','blue');
Etiquetas:
javascript,
jQuery,
programación web
Evento onChange de un select con jQuery
Por lo general, al asociar eventos con el magnífico, glorioso, inimitable jQuery, usaríamos la función bind, pero en el caso del evento onChange de los select debemos hacerlo de otra manera.
var selectEscogido=$('#select-a-escoger');
$(selectEscogido).change(function(){
//instrucciones
}
var selectEscogido=$('#select-a-escoger');
$(selectEscogido).change(function(){
//instrucciones
}
Etiquetas:
javascript,
jQuery,
php,
programación web
lunes, 20 de julio de 2009
Problema "Fatal error: allowed memory size of X exhausted"
Me ha surgido en el trabajo el siguiente problema. No me voy a molestar en cambiar demasiado el correo interno que sugería la solución.
El bloque de código que fallaba estaba localizado en otra parte de gd.func.php, al habernos visto forzados a resamplear las imágenes de otra manera (mediante la opción ‘2’ de las opciones de campo en el gestor). He comprobado que la imagen sí que conseguía subirla. Resampleaba el thumb y lo copiaba correctamente a su carpeta, pero fallaba al resamplear la imagen normal y dejaba el archivo sin resamplear dentro de uploads. De nuevo, el problema se concentraba en la función imagecreatetruecolor, pero en este caso no parecía deberse a un error de código.
Activé el ‘reporting’ de errores mediante ini_set(“display_errors”, 1); error_reporting (E_ALL | E_STRICT); , lo que me permitió ver que se estaba produciendo el siguiente error fatal:
Fatal error: allowed memory size of X exhausted (tried to allocate 49 bytes), siendo X un número cercano a 17M.
Parece que el servidor tiene asociada una memoria de trabajo baja que provoca errores en funciones que consumen considerable cantidad, como imagecreatetruecolor. Por fortuna, en este caso podemos establecer la variable memory_limit de php.ini a una cantidad superior para que tenga más margen. He corregido el problema, de momento, poniendo la siguiente orden antes de la llamada a la función imagetruecolor que fallaba:
ini_set (“memory_limit”, “20M”);
Si vuelve a fallar, habría que comprobar cuánta memoria está usando y cuánta más necesitaría, y en caso de que ya no se pueda andar cambiando los parámetros por ini_set no quedaría más remedio que sugerir al hosting que eche una mano.
El bloque de código que fallaba estaba localizado en otra parte de gd.func.php, al habernos visto forzados a resamplear las imágenes de otra manera (mediante la opción ‘2’ de las opciones de campo en el gestor). He comprobado que la imagen sí que conseguía subirla. Resampleaba el thumb y lo copiaba correctamente a su carpeta, pero fallaba al resamplear la imagen normal y dejaba el archivo sin resamplear dentro de uploads. De nuevo, el problema se concentraba en la función imagecreatetruecolor, pero en este caso no parecía deberse a un error de código.
Activé el ‘reporting’ de errores mediante ini_set(“display_errors”, 1); error_reporting (E_ALL | E_STRICT); , lo que me permitió ver que se estaba produciendo el siguiente error fatal:
Fatal error: allowed memory size of X exhausted (tried to allocate 49 bytes), siendo X un número cercano a 17M.
Parece que el servidor tiene asociada una memoria de trabajo baja que provoca errores en funciones que consumen considerable cantidad, como imagecreatetruecolor. Por fortuna, en este caso podemos establecer la variable memory_limit de php.ini a una cantidad superior para que tenga más margen. He corregido el problema, de momento, poniendo la siguiente orden antes de la llamada a la función imagetruecolor que fallaba:
ini_set (“memory_limit”, “20M”);
Si vuelve a fallar, habría que comprobar cuánta memoria está usando y cuánta más necesitaría, y en caso de que ya no se pueda andar cambiando los parámetros por ini_set no quedaría más remedio que sugerir al hosting que eche una mano.
miércoles, 24 de junio de 2009
Función muy útil para "resumir" párrafos que no queremos mostrar largos en la página. Añadirá puntos suspensivos y evita cortar texto por la mitad para que no quede '&aa' de 'á' y cosas similares.
function recortaTexto($string, $maxlen, $addon = '...')
{
if(strlen(strip_tags($string)) <= $maxlen)
{ // Ha de ser HTML válido
return $string;
}
//Tengo que comprobar que el $maxlen no esté cortando un á o algo similar, así que aumento
// maxlen hasta que el siguiente caracter sea un espacio
while ($string[$maxlen+2]!=' '){
$maxlen+=1;
}
$closing_tags = array();
$string = str_replace(chr(0), '', $string);
$string = preg_replace('/(<\/?[a-z][a-z0-9]*[^<>]*>)/im', chr(0) . '\\1' . chr(0), $string);
$result = explode(chr(0), $string);
for($i = 0, $n = count($result), $len = 0; $i < $n; $i++)
{
if($i % 2)
{
if(substr($result[$i], -2, 1) == '/')
{
continue;
}
if($result[$i]{1} == '/')
{
unset($closing_tags[array_search($result[$i], $closing_tags)]);
continue;
}
$closing_tags[$i] = '' . ((strpos($result[$i], ' ') === false) ? substr($result[$i], 1, -1) : substr($result[$i], 1, strpos($result[$i], ' ') - 1)) . '>';
}
else
{
$len += strlen($result[$i]);
if($len >= $maxlen)
{
$len -= strlen($result[$i]);
break;
}
}
}
return implode('', array_slice($result, 0, $i)) . substr($result[$i], 0, $maxlen - $len) . $addon . implode(array_reverse($closing_tags));
}
function recortaTexto($string, $maxlen, $addon = '...')
{
if(strlen(strip_tags($string)) <= $maxlen)
{ // Ha de ser HTML válido
return $string;
}
//Tengo que comprobar que el $maxlen no esté cortando un á o algo similar, así que aumento
// maxlen hasta que el siguiente caracter sea un espacio
while ($string[$maxlen+2]!=' '){
$maxlen+=1;
}
$closing_tags = array();
$string = str_replace(chr(0), '', $string);
$string = preg_replace('/(<\/?[a-z][a-z0-9]*[^<>]*>)/im', chr(0) . '\\1' . chr(0), $string);
$result = explode(chr(0), $string);
for($i = 0, $n = count($result), $len = 0; $i < $n; $i++)
{
if($i % 2)
{
if(substr($result[$i], -2, 1) == '/')
{
continue;
}
if($result[$i]{1} == '/')
{
unset($closing_tags[array_search($result[$i], $closing_tags)]);
continue;
}
$closing_tags[$i] = '' . ((strpos($result[$i], ' ') === false) ? substr($result[$i], 1, -1) : substr($result[$i], 1, strpos($result[$i], ' ') - 1)) . '>';
}
else
{
$len += strlen($result[$i]);
if($len >= $maxlen)
{
$len -= strlen($result[$i]);
break;
}
}
}
return implode('', array_slice($result, 0, $i)) . substr($result[$i], 0, $maxlen - $len) . $addon . implode(array_reverse($closing_tags));
}
viernes, 12 de junio de 2009
Caracteres  y problema "headers already sent"
He estado una temporada codificando con Aptana Studios y teniendo que usar el notepad para editar ciertos scripts, ya que Aptana parece completamente incapaz de mostrar caracteres latinos por mucho que intente modificarlo, motivo por el que voy a cambiar por completo a Zend Studio.
De vez en cuando las páginas me daban un error relativo al session_start sugiriendo que los headers ya se habían mostrado (headers already sent). Tras investigar, vi que se trataba de o bien el notepad estaba dejando algún carácter de codificación delante del primer carácter real o algo raro estaba pasando con la transición al Aptana posteriormente. Hoy empezó a aparecer además la combinación . Descubrí que si guardaba con el Wordpad el problema se solucionaba inmediatamente.
El notepad parece que guarda por defecto en UTF8, lo que provoca conflicto con el ISO-8859-1. Voy a recalcar, por lo tanto, que JAMÁS hay que editar un script en el notepad aunque sea una urgencia o parezca no haber alternativa de momento. Ahora estoy probando el PSPad, que será, asumo, competente.
De vez en cuando las páginas me daban un error relativo al session_start sugiriendo que los headers ya se habían mostrado (headers already sent). Tras investigar, vi que se trataba de o bien el notepad estaba dejando algún carácter de codificación delante del primer carácter real o algo raro estaba pasando con la transición al Aptana posteriormente. Hoy empezó a aparecer además la combinación . Descubrí que si guardaba con el Wordpad el problema se solucionaba inmediatamente.
El notepad parece que guarda por defecto en UTF8, lo que provoca conflicto con el ISO-8859-1. Voy a recalcar, por lo tanto, que JAMÁS hay que editar un script en el notepad aunque sea una urgencia o parezca no haber alternativa de momento. Ahora estoy probando el PSPad, que será, asumo, competente.
lunes, 8 de junio de 2009
viernes, 29 de mayo de 2009
Comprobar si una dirección de correo es correcta
if (!eregi("^[A-Z0-9._%-]+@[A-Z0-9._%-]+\.[A-Z]{2,4}$", $_POST[email])) {
echo "
}
echo "
El campo Email es incorrecto
";}
martes, 24 de marzo de 2009
Listar los archivos de un directorio
El directorio del ejemplo es 'imagenes/fotos_casas'.
if (is_dir("imagenes/fotos_casas")){
$scanarray=scandir("imagenes/fotos_casas");
for ($i = 0; $i < count($scanarray); $i++){
if ($scanarray[$i] != "." && $scanarray[$i] != ".."){
//Asegurarse de que sea un archivo y no un directorio.
if (is_file("imagenes/fotos_casas/" . $scanarray[$i])){
//echo $scanarray[$i]
}
}
}
}
if (is_dir("imagenes/fotos_casas")){
$scanarray=scandir("imagenes/fotos_casas");
for ($i = 0; $i < count($scanarray); $i++){
if ($scanarray[$i] != "." && $scanarray[$i] != ".."){
//Asegurarse de que sea un archivo y no un directorio.
if (is_file("imagenes/fotos_casas/" . $scanarray[$i])){
//echo $scanarray[$i]
}
}
}
}
Etiquetas:
ficheros,
php,
programación web
Ajax y problemas con el encoding
En algunos casos, aunque la base de datos parezca correcta, los navegadores no muestran bien los datos o tienen problemas con la codificación cuando se usan técnicas de Ajax. Para solucionarlo, hay que asegurarse de que las pequeñas páginas php que se encargan de realizar las operaciones de Ajax lleven un header apropiado. Además, conviene convertir los $_GET y los $_POST que se pasen desde javascript al charset correcto:
header('Content-type: text/html; charset=iso-8859-1');
//+
$texto = iconv("UTF-8", "iso-8859-1", $_POST['texto']);
header('Content-type: text/html; charset=iso-8859-1');
//+
$texto = iconv("UTF-8", "iso-8859-1", $_POST['texto']);
Etiquetas:
ajax,
charset,
compatibilidad,
javascript,
programación web
martes, 17 de marzo de 2009
Formatear integrales a euros usando PHP
Formateará la variable precio a euros con puntos para los miles y dos decimales.
number_format($precio, 2, ',', '.')
number_format($precio, 2, ',', '.')
lunes, 16 de marzo de 2009
Tamaño base del texto igual en todos los navegadores
Dado que ciertos navegadores toman un tamaño de texto nombrado como un tamaño y otros navegadores lo toman de otra manera, es necesario utilizar el siguiente hack:
body {
font-size: small;
}
* html body {
font-size: x-small; /* for IE5/Win */
f\ont-size: small; /* for other IE versions */
}
body {
font-size: small;
}
* html body {
font-size: x-small; /* for IE5/Win */
f\ont-size: small; /* for other IE versions */
}
miércoles, 4 de marzo de 2009
Limpiar floats que no se limpian con clear both
Por algún motivo, clear: both no acaba de funcionar cuando hay diferentes combinaciones de floats dentro del contenedor. Para solucionarlo, se puede probar a utilizar los siguientes estilos de CSS para el contenedor que contiene los floats:
overflow: auto;
height: auto;
overflow: auto;
height: auto;
viernes, 27 de febrero de 2009
Trabajo con ficheros en PHP
Para leer el contenido de un directorio, pero sólo los ficheros o directorios (sin "." ni ".."):
$directorio=dir(RUTA);
while ($fichero=$directorio->read()){
//Mientras siga pudiendo leer.
//Siempre que el fichero no sea ni "." ni ".."
if ($fichero!='.' && $fichero!='..'){
//trabajo con el fichero
}
}
Para leer el fichero en bloques de 8kb (más seguro que leerlo de una vez):
$file=fopen(RUTA_FICHERO);
if ($file!=0){
//No ha dado error.
//Escribo el contenido.
$contenido = '';
while (!feof($file)) {
$contenido .= fread($file, 8192);
}
//Hago lo que tenga que hacer con el contenido del fichero.
fclose($file);
$directorio=dir(RUTA);
while ($fichero=$directorio->read()){
//Mientras siga pudiendo leer.
//Siempre que el fichero no sea ni "." ni ".."
if ($fichero!='.' && $fichero!='..'){
//trabajo con el fichero
}
}
Para leer el fichero en bloques de 8kb (más seguro que leerlo de una vez):
$file=fopen(RUTA_FICHERO);
if ($file!=0){
//No ha dado error.
//Escribo el contenido.
$contenido = '';
while (!feof($file)) {
$contenido .= fread($file, 8192);
}
//Hago lo que tenga que hacer con el contenido del fichero.
fclose($file);
Etiquetas:
ficheros,
php,
programación web
martes, 24 de febrero de 2009
Interfaces en PHP 5
La siguiente es la estructura de un interface común:
interface BeingDamage{
public function processDamage($weapon);
public function damageHealth($damage);
}
El interfaz se implementaría de la siguiente manera:
class character implements BeingDamage{
function processDamage($weapon){
//implementación
}
function damageHealth($damage){
//implementación
}
}
Es importante recordar que la reutilización de clases y métodos sugiere que conviene programar a interfaces, no a implementaciones.
interface BeingDamage{
public function processDamage($weapon);
public function damageHealth($damage);
}
El interfaz se implementaría de la siguiente manera:
class character implements BeingDamage{
function processDamage($weapon){
//implementación
}
function damageHealth($damage){
//implementación
}
}
Es importante recordar que la reutilización de clases y métodos sugiere que conviene programar a interfaces, no a implementaciones.
miércoles, 18 de febrero de 2009
Solucionar compatibilidades entre navegadores en CSS
Para evitar problemas de compatibilidades entre diferentes navegadores, sobre todo en cuanto se usen listas, conviene añadir lo siguiente a la hoja de estilos CSS:
* { margin:0; padding:0; border: 0; }
* { margin:0; padding:0; border: 0; }
Etiquetas:
compatibilidad,
css,
programación web
martes, 17 de febrero de 2009
Extraer información de un archivo de texto formateado de cierta manera
Organizo valores constantes mediante archivos ini para poder modificarlos rápidamente y que afecten diferentes sitios. Uso la siguiente función:
function devuelveValorIni($claveIni,$archivoIni){
$datos=archivoTextoAString("/ini/" . $archivoIni);
//Queda separarlo en función de los delimitadores y del texto.
$textoConvertido="[" . $claveIni;
$valorIni=extraeUnidad($datos,$textoConvertido,"]");
return $valorIni;
}
function archivoTextoAString($archivo){
//Se busca en el archivo ini los hit die para esa clase especificada.
$directorioBase=parent_directory();
// read file into string
$data = file_get_contents($directorioBase . $archivo) or die("Could not read file!");
//Teniendo ya el archivo en un string, hago la búsqueda y separación.
return $data;
}
function parent_directory(){
return dirname(dirname(__FILE__));
}
Asumo la siguiente estructura:
[LIMITEMAPA:150]
Se establece que el primer límite donde empezar a buscar es '[LIMITEMAPA:' y el último es el cierre, ']'.
function devuelveValorIni($claveIni,$archivoIni){
$datos=archivoTextoAString("/ini/" . $archivoIni);
//Queda separarlo en función de los delimitadores y del texto.
$textoConvertido="[" . $claveIni;
$valorIni=extraeUnidad($datos,$textoConvertido,"]");
return $valorIni;
}
function archivoTextoAString($archivo){
//Se busca en el archivo ini los hit die para esa clase especificada.
$directorioBase=parent_directory();
// read file into string
$data = file_get_contents($directorioBase . $archivo) or die("Could not read file!");
//Teniendo ya el archivo en un string, hago la búsqueda y separación.
return $data;
}
function parent_directory(){
return dirname(dirname(__FILE__));
}
Asumo la siguiente estructura:
[LIMITEMAPA:150]
Se establece que el primer límite donde empezar a buscar es '[LIMITEMAPA:' y el último es el cierre, ']'.
Funciones JavaScript no definidas y Firebug
A menudo una página no funciona como debería y Firebug nos devuelve que una función de JavaScript está "not defined" o "undefined". Uno tiende a pensar que se debería a que no tiene acceso a la función y el tag script no ha funcionado bien por algún motivo, pero en realidad se suele deber a un error de sintaxis. Asegúrate de que el código javascript sea correcto. También suele pasar cuando desde un archivo .js llamas a funciones de otro (como librerías Prototype).
Etiquetas:
javascript,
programación web
Pasar un array de PHP a JavaScript mediante JSON
Por algún motivo, pasar arrays de PHP a JavaScript es más complicado de lo que debería ser. Por fortuna, para arreglar el asunto podemos traducir el array de PHP a JSON.
Cualquier servidor de PHP mayor que el 5 tiene la siguiente función:
$arrayAJS=json_encode($arrayEnPHP);
Nota muy importante: nada más pasar el array PHP a JSON y enviarlo como parámetro, la función javascript lo considera un array normal y corriente. No hace falta deserializarlo.
Cualquier servidor de PHP mayor que el 5 tiene la siguiente función:
$arrayAJS=json_encode($arrayEnPHP);
Nota muy importante: nada más pasar el array PHP a JSON y enviarlo como parámetro, la función javascript lo considera un array normal y corriente. No hace falta deserializarlo.
Etiquetas:
ajax,
javascript,
json,
php,
programación web
Estilo de menús horizontales en CSS usando ambos navegadores
Uno de los elementos específicos que siempre crea problemas de compatibilidad entre los dos navegadores principales, Firefox y IE, son los menús horizontales. Hay que jugar con la propiedad margin tanto de esa capa como la que le precede, intentando combinaciones de números positivos y negativos en ambos para acabar ajustando ambos. Por ejemplo:
#PaisesContenedorLogo{
width: 700px;
height: 132px;
background-color: #888888;
background: url("../img/logo.jpg");
background-repeat: no-repeat;
margin-bottom: 16px;
}
#PaisesContenedorMenu{
background-color: #95b48b;
color: #000000;
height: 30px;
margin-top: -16px;
}
En el caso expuesto, 'PaisesContenedorLogo' está situada por encima de 'PaisesContenedorMenu' y en la misma capa, pero, como se ve, los margin no parecen muy racionales.
#PaisesContenedorLogo{
width: 700px;
height: 132px;
background-color: #888888;
background: url("../img/logo.jpg");
background-repeat: no-repeat;
margin-bottom: 16px;
}
#PaisesContenedorMenu{
background-color: #95b48b;
color: #000000;
height: 30px;
margin-top: -16px;
}
En el caso expuesto, 'PaisesContenedorLogo' está situada por encima de 'PaisesContenedorMenu' y en la misma capa, pero, como se ve, los margin no parecen muy racionales.
Etiquetas:
compatibilidad,
css,
programación web
lunes, 16 de febrero de 2009
Crear un array unidimensional en JSON
No confundir con los arrays asociativos.
{
"arrayUnidimensional" : [0,0,2,3,0,1,0,5]
}
{
"arrayUnidimensional" : [0,0,2,3,0,1,0,5]
}
Crear Request Object para recibir JSON usando AJAX
Omito el lado del servidor, pero el archivo php debe generar un texto que respete la sintaxis.
var the_object;Es conveniente limpiar la cadena enviada por el servidor para evitar posibles inyecciones de código.
var http_request = new XMLHttpRequest();
http_request.open( "GET", url, true );
http_request.send(null);
http_request.onreadystatechange = function () {
if ( http_request.readyState == 4 ) {
if ( http_request.status == 200 ) {
var inputJSON=http_request.responseText;
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
eval('(' + inputJSON + ')');
} else {
alert( "There was a problem with the URL." );
}
http_request = null;
}
};
Función para crear un XML Request Object válido
function devuelveXMLRequestObject {
xmlhttp=false;
this.requestFile = file;
this.encodeURIString = true;
this.execute = false;
this.AjaxFailedAlert = "Funcionalidades no soportadas.";
if (window.XMLHttpRequest) {
this.xmlhttp = new XMLHttpRequest();
if (this.xmlhttp.overrideMimeType) {
this.xmlhttp.overrideMimeType('text/xml');
}
}
else if (window.ActiveXObject) {
//Internet Explorer
try {
this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}catch (e) {
try {
this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
this.xmlhttp = null;
}
}
if (!this.xmlhttp && typeof XMLHttpRequest!='undefined') {
this.xmlhttp = new XMLHttpRequest();
if (!this.xmlhttp){
this.failed = true;
}
}
}
return this.xmlhttp ;
}
Ejemplo de envío de datos mediante POST en AJAX
A pesar de la carga al servidor que implica, a menudo conviene enviar los parámetros para una operación mediante POST en vez de GET, ya que el GET escribe los parámetros en la url.
Para realizar una Request usando post, también debemos componer la lista de de parámetros como quedaría en la URL:
campo1=valor
$campo2=valor
Por ejemplo:
'campo1='+document.getElementById('campo1').value
+ '&campo2='+document.getElementById('campo2').value
Posteriormente, debemos enviar esos valores mediante la función send del objeto request.
ajax.open ('POST', url, true);
ajax.onreadystatechange=funcion;
ajax.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
ajax.send(valores);
El setRequestHeader es útil en caso de que el Content-Type se quiera cambiar a XML en vez de dejarlo por defecto en el fichero php al que se llame, por razones de prueba.
Para realizar una Request usando post, también debemos componer la lista de de parámetros como quedaría en la URL:
campo1=valor
$campo2=valor
Por ejemplo:
'campo1='+document.getElementById('campo1').value
+ '&campo2='+document.getElementById('campo2').value
Posteriormente, debemos enviar esos valores mediante la función send del objeto request.
ajax.open ('POST', url, true);
ajax.onreadystatechange=funcion;
ajax.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
ajax.send(valores);
El setRequestHeader es útil en caso de que el Content-Type se quiera cambiar a XML en vez de dejarlo por defecto en el fichero php al que se llame, por razones de prueba.
Etiquetas:
ajax,
javascript,
php,
programación web
Sumario de AJAX
Qué constituye el corazón de AJAX y la diferencia con la programación tradicional de páginas web:
1) El usuario visita la página
2) El usuario escribe algo en una caja de texto, pulsa un botón o arrastra algo con el cursor
3) Lo que el usuario haga en el punto 2 provoca que JavaScript envíe una petición al servidor
4) El servidor procesa la petición (usando PHP, por ejemplo), devolviendo algún dato
5) JavaScript recibe la información y la usa para actualizar la página, sin cargarla otra vez
1) El usuario visita la página
2) El usuario escribe algo en una caja de texto, pulsa un botón o arrastra algo con el cursor
3) Lo que el usuario haga en el punto 2 provoca que JavaScript envíe una petición al servidor
4) El servidor procesa la petición (usando PHP, por ejemplo), devolviendo algún dato
5) JavaScript recibe la información y la usa para actualizar la página, sin cargarla otra vez
sábado, 14 de febrero de 2009
Palabras reservadas que pueden crear conflicto con PEAR
De momento he encontrado las siguientes:
GROUP
INT
La solución más sencilla es añadir a los campos con ese nombre un guión bajo al final.
GROUP
INT
La solución más sencilla es añadir a los campos con ese nombre un guión bajo al final.
Error de sintaxis en MySQL usando PEAR
Con el siguiente código:
function executeCommand( $sql, $args )
{
global $db;
$sth = $db->prepare( $sql );
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
echo "
$res=& $db->execute( $sth, $args );
if (PEAR::isError($res)) {
die($res->getMessage());
}
print_r($res);
return $res;
}
Si devuelve un error como éste: 'DB Error: syntax error', puede darse el caso de que alguno de los campos de la tabla se llamen igual que una palabra reservada. En mi caso había un campo llamado group, que obviamente entraba en conflicto con la palabra reservada GROUP. Es bueno tenerlo en cuenta cuando parece que las cosas no van sin motivo alguno.
function executeCommand( $sql, $args )
{
global $db;
$sth = $db->prepare( $sql );
if (PEAR::isError($sth)) {
die($sth->getMessage());
}
echo "
$sth de la sql $sql
";$res=& $db->execute( $sth, $args );
if (PEAR::isError($res)) {
die($res->getMessage());
}
print_r($res);
return $res;
}
Si devuelve un error como éste: 'DB Error: syntax error', puede darse el caso de que alguno de los campos de la tabla se llamen igual que una palabra reservada. En mi caso había un campo llamado group, que obviamente entraba en conflicto con la palabra reservada GROUP. Es bueno tenerlo en cuenta cuando parece que las cosas no van sin motivo alguno.
Referencia al último ID creado por una consulta INSERT en MySQL
last_insert_id[ ]
Con paréntesis, que en caso contrario molesta a los scripts del blog.
viernes, 13 de febrero de 2009
Tabulación simple con CSS
|span style='margin-left:20px;'>
Sustituir la barra vertical del comienzo por < .
Sustituir la barra vertical del comienzo por < .
lunes, 9 de febrero de 2009
Convertir archivo de texto plano a string y guardarlo en memoria
// set file to read
$filename = "mindspace.txt";// read file into string
$data = file_get_contents($filename) or die("Could not read file!");
Funciones para seleccionar datos usando PHP y MySQL
Estas funciones facilitan la extracción de datos de una base de datos MySQL usando PHP.
function conectar(){
//Conecta y devuelve la variable conexión.
$conn = mysql_connect("localhost", tu_usuario, tu_password);
mysql_select_db(tu_db);
return $conn;
}
function escribeSelectSQL($campos,$tablas,$condicion){
$consulta="SELECT ";
//añado tantos campos como me haya enviado.
$limiteCampos=count($campos);
$limiteTablas=count($tablas);
for($contador=1;$contador<=$limiteCampos;$contador+=1){
$consulta=$consulta . $campos[$contador-1];
if ($contador<$limiteCampos){
//todavía quedan campos, así que habrá que añadir una coma.
$consulta=$consulta . ", ";
}else{
//añado un espacio, por si acaso.
$consulta=$consulta . " ";
}
} //fin del for para los campos.
$consulta=$consulta . " FROM ";
for($contador=1;$contador<=$limiteTablas;$contador+=1){
$consulta=$consulta . $tablas[$contador-1];
if ($contador<$limiteTablas){
//todavía quedan campos, así que habrá que añadir una coma.
$consulta=$consulta . ", ";
}else{
//añado un espacio, por si acaso.
$consulta=$consulta . " ";
}
} //fin del for para las tablas.
//Queda añadir la cláusula WHERE y la condición.
$consulta=$consulta . " WHERE " . $condicion;
//echo ("
$sql = mysql_query($consulta);
return $sql;
}
function devuelveRegistros($sql){
//pasada una SQL válida de selección, devolvemos los registros.
return mysql_fetch_array($sql);
}
function cierraConexion($conn){
mysql_close($conn);
}
function conectar(){
//Conecta y devuelve la variable conexión.
$conn = mysql_connect("localhost", tu_usuario, tu_password);
mysql_select_db(tu_db);
return $conn;
}
function escribeSelectSQL($campos,$tablas,$condicion){
$consulta="SELECT ";
//añado tantos campos como me haya enviado.
$limiteCampos=count($campos);
$limiteTablas=count($tablas);
for($contador=1;$contador<=$limiteCampos;$contador+=1){
$consulta=$consulta . $campos[$contador-1];
if ($contador<$limiteCampos){
//todavía quedan campos, así que habrá que añadir una coma.
$consulta=$consulta . ", ";
}else{
//añado un espacio, por si acaso.
$consulta=$consulta . " ";
}
} //fin del for para los campos.
$consulta=$consulta . " FROM ";
for($contador=1;$contador<=$limiteTablas;$contador+=1){
$consulta=$consulta . $tablas[$contador-1];
if ($contador<$limiteTablas){
//todavía quedan campos, así que habrá que añadir una coma.
$consulta=$consulta . ", ";
}else{
//añado un espacio, por si acaso.
$consulta=$consulta . " ";
}
} //fin del for para las tablas.
//Queda añadir la cláusula WHERE y la condición.
$consulta=$consulta . " WHERE " . $condicion;
//echo ("
DEBUG: la consulta formada es $consulta.
");$sql = mysql_query($consulta);
return $sql;
}
function devuelveRegistros($sql){
//pasada una SQL válida de selección, devolvemos los registros.
return mysql_fetch_array($sql);
}
function cierraConexion($conn){
mysql_close($conn);
}
Clase y números aleatorios en PHP
Digamos que queremos crear una clase en php para un juego de rol y pretendemos que sus abilidades se generen automáticamente, pudiendo valer de 8 a 18.
class cBeing{
var $IdBeing;
var $Nombre;
var $Apellidos;
var $Str;
var $Con;
var $Int;
var $Wis;
var $Cha;
function cBeing(){
//Constructor por defecto de Being.
$this->Str=12;
$this->Con=12;
$this->Int=12;
$this->Wis=12;
$this->Cha=12;
}
function rollAbilities(){
//Función que genera valores aleatorios para las cuatro abilidades principales.
$this->Str=devuelveAleatorio18();
$this->Con=devuelveAleatorio18();
$this->Int=devuelveAleatorio18();
$this->Wis=devuelveAleatorio18();
$this->Cha=devuelveAleatorio18();
}
function devuelveAleatorio18(){
$random = mt_rand(8,18);
return $random;
}
}
class cBeing{
var $IdBeing;
var $Nombre;
var $Apellidos;
var $Str;
var $Con;
var $Int;
var $Wis;
var $Cha;
function cBeing(){
//Constructor por defecto de Being.
$this->Str=12;
$this->Con=12;
$this->Int=12;
$this->Wis=12;
$this->Cha=12;
}
function rollAbilities(){
//Función que genera valores aleatorios para las cuatro abilidades principales.
$this->Str=devuelveAleatorio18();
$this->Con=devuelveAleatorio18();
$this->Int=devuelveAleatorio18();
$this->Wis=devuelveAleatorio18();
$this->Cha=devuelveAleatorio18();
}
function devuelveAleatorio18(){
$random = mt_rand(8,18);
return $random;
}
}
Suscribirse a:
Entradas (Atom)