<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Blog ElCodiguero</title><link>http://blog.elcodiguero.com/</link><description></description><atom:link href="http://blog.elcodiguero.com/rss/todo.xml" rel="self"></atom:link><lastBuildDate>Tue, 27 Sep 2011 23:44:14 -0300</lastBuildDate><item><title>Slackware 13.37: Instalación y configuración básica</title><link>http://blog.elcodiguero.com/linux-unix/slackware-1337-instalacion-y-configuracion-basica.html</link><description>&lt;div class="figure align-center"&gt;
&lt;img alt="Slackware" src="/imagenes/slackware.png" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.slackware.com"&gt;Slackware&lt;/a&gt; es la distribución de &lt;a class="reference external" href="http://es.wikipedia.org/wiki/GNU/Linux"&gt;&lt;span class="caps"&gt;GNU&lt;/span&gt;/Linux&lt;/a&gt; más antigua entre las
que siguen desarrollándose. Se caracteriza por su fama de complicada,
sin embargo una vez que el sistema está configurado el mantenimiento
necesario es mínimo, y resulta tan sencilla de manejar como cualquier
otra distribución. Lo único que el sistema pide a cambio de su
estabilidad y facilidad de mantenimiento es algo de tiempo leyendo
manuales y ayudas para comprender lo que sucede y por qué. La
instalación, configuración y uso de este sistema es probablemente la
mejor forma de aprender a manejar un sistema&amp;nbsp;Unix.&lt;/p&gt;
&lt;p&gt;Este artículo es una actualización del &lt;a class="reference external" href="/linux-unix/slackware-131-instalacion-y-configuracion-basica.html"&gt;anterior sobre Slackware 13.1&lt;/a&gt;,
con cosas nuevas para&amp;nbsp;13.37.&lt;/p&gt;
&lt;div class="section" id="instalacion"&gt;
&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;La instalación en sí no es un problema, recomiendo &lt;a class="reference external" href="http://manual.zenwalk.org/manual_es.pdf"&gt;esta guía de
instalación&lt;/a&gt; para aquellos que no hayan realizado este proceso antes.
La guía es para la distribución &lt;a class="reference external" href="http://www.zenwalk.org"&gt;ZenWalk&lt;/a&gt;, pero el proceso es casi el
mismo ya que &lt;span class="raw-html"&gt;&lt;span lang="en"&gt;ZenWalk&lt;/span&gt;&lt;/span&gt; es una de las
tantas distribuciones derivadas de&amp;nbsp;Slackware.&lt;/p&gt;
&lt;p&gt;Para iniciar el proceso, arranca con el &lt;span class="caps"&gt;DVD&lt;/span&gt; de Slackware y sigue las
instrucciones en pantalla. Una vez iniciada la consola de root, ejecuta
el programa &lt;strong&gt;setup&lt;/strong&gt;. Una vez aquí, debe iniciarse la instalación
eligiendo la opción &lt;strong&gt;&lt;span class="caps"&gt;ADDSWAP&lt;/span&gt;&lt;/strong&gt; en el menú, o &lt;strong&gt;&lt;span class="caps"&gt;TARGET&lt;/span&gt;&lt;/strong&gt;. Estos pasos
deben hacerse primero ya que son los que preparan el disco en el que se
instalará, si no se hacen primero siempre se llega a un punto en la
instalación en el que se debe iniciar todo el proceso de&amp;nbsp;nuevo.&lt;/p&gt;
&lt;p&gt;Y esto siempre requiere salir del programa de instalación y abrirlo de
nuevo, ya que la autodetección del disco &lt;span class="caps"&gt;CD&lt;/span&gt;/&lt;span class="caps"&gt;DVD&lt;/span&gt; de instalación funciona
solamente la primera vez que se&amp;nbsp;hace.&lt;/p&gt;
&lt;p&gt;De todas formas, la instalación con las opciones por defecto
(&lt;span class="raw-html"&gt;&lt;span lang="en"&gt;full install&lt;/span&gt;&lt;/span&gt;, instalar todos los
paquetes disponibles) puede iniciarse en menos de 5 minutos y terminarse
en más o menos media&amp;nbsp;hora.&lt;/p&gt;
&lt;p&gt;Al terminar la copia de archivos se hacen algunos pasos para la
configuración inicial del sistema, principalmente lo que respecta a la
ubicación geográfica, la configuración de red y la instalación del
gestor de arranque (&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Lilo_(Linux)"&gt;&lt;span class="caps"&gt;LILO&lt;/span&gt;&lt;/a&gt;). Una vez finalizados estos pasos, el
programa de instalación se cierra y se nos indica que debemos reiniciar
el&amp;nbsp;sistema.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuracion-inicial"&gt;
&lt;h2&gt;Configuración&amp;nbsp;inicial&lt;/h2&gt;
&lt;p&gt;Al arrancar el sistema, a diferencia de lo que sucede con otras
distribuciones, el entorno gráfico no se inicia por defecto. Se nos
presenta una pantalla de inicio de sesión en modo texto, en la que
deberemos entrar como el usuario root y con la contraseña
correspondiente (que habremos indicado en uno de los últimos pasos de la&amp;nbsp;instalación).&lt;/p&gt;
&lt;p&gt;Una vez dentro, hay varias tareas a&amp;nbsp;realizar.&lt;/p&gt;
&lt;div class="section" id="arrancar-por-defecto-el-entorno-grafico"&gt;
&lt;h3&gt;Arrancar por defecto el entorno&amp;nbsp;gráfico&lt;/h3&gt;
&lt;p&gt;Esto se hace editando el archivo /etc/inittab y cambiando el &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Nivel_de_ejecución"&gt;nivel de
ejecución&lt;/a&gt; predeterminado. Lo haremos con &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Vim"&gt;vim&lt;/a&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
root&amp;#64;alvaro:/# vim /etc/inittab
&lt;/pre&gt;
&lt;p&gt;Una vez que se abra el archivo, hay que ir a la línea que&amp;nbsp;contiene&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Default runlevel. (Do not set to 0 or 6)
id:3:initdefault:
&lt;/pre&gt;
&lt;p&gt;vim no se parece en nada a los editores normales, por lo que para quien
lo usa por primera vez puede resultar bastante extraño. La forma más
sencilla de editar lo necesario y salir es llevar el cursor hacia el
&lt;strong&gt;3&lt;/strong&gt; en la línea mencionada antes, y presionar la siguiente secuencia
de&amp;nbsp;teclas:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;r&lt;/strong&gt; : Pondrá el editor en el modo adecuado para reemplazar un&amp;nbsp;caracter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4&lt;/strong&gt; : escribirá un 4, reemplazando al&amp;nbsp;3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;:wq&lt;/strong&gt; : este comando guarda el archivo y sale de&amp;nbsp;vim&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Con esto, se indica que el nivel de ejecución por defecto debe ser el
nivel 4. Normalmente cualquier documentación de &lt;span class="caps"&gt;GNU&lt;/span&gt;/Linux que se
encuentre indica que el nivel 4 no se utiliza, y además casi todas las
demás distribuciones utilizan el nivel 5 para el modo gráfico. En esto
Slackware es diferente, en tanto que utiliza el nivel 4 para el modo
gráfico y no utiliza el nivel&amp;nbsp;5.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="crear-un-usuario-sin-privilegios"&gt;
&lt;h3&gt;Crear un usuario sin&amp;nbsp;privilegios&lt;/h3&gt;
&lt;p&gt;La forma usual de utilizar el sistema es como usuario sin privilegios.
Esto es, sin permiso para instalar programas o modificar el sistema,
únicamente se tiene permiso para hacer modificaciones en la carpeta
asignada al usuario. Este modo es ideal por la seguridad y para evitar
errores fatales, que casi todos hemos cometido alguna vez. Añadiremos un
usuario, entonces, con el comando adduser y escribiendo la información
que el comando&amp;nbsp;solicita.&lt;/p&gt;
&lt;p&gt;Para que este usuario sea usable, es necesario añadirlo a varios grupos:
audio (para que pueda utilizar la tarjeta de sonido), cdrom, disk,
plugdev (para que pueda utilizar dispositivos extraíbles), y video. Esto
se puede hacer facilmente; en uno de los pasos adduser muestra la
siguiente&amp;nbsp;línea:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Additional groups (comma separated) []:
&lt;/pre&gt;
&lt;p&gt;Si se presiona la flecha hacia arriba, se obtiene una lista de los
grupos&amp;nbsp;necesarios.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="utilizar-el-kernel-generic-en-lugar-de-huge"&gt;
&lt;h3&gt;Utilizar el kernel generic en lugar de&amp;nbsp;huge&lt;/h3&gt;
&lt;p&gt;Para la instalación, y por defecto en la instalación completa, Slackware
instala un núcleo Linux que tiene compilado casi todos los controladores
disponibles. Esto implica que el núcleo por defecto es bastante grande e
ineficiente, aunque en una instalación funciona bien. Para el uso diario
es mejor cambiar a un núcleo que incluya solamente lo mínimo, y en el
que los controladores estén incluidos en forma de módulos (de tal forma
que solamente se cargarán aquellos que sean necesarios).
Este cambio requiere de varias modificaciones, que quizás sea mejor
hacer desde el entorno gráfico (se requiere edición de archivos, y es
más fácil usar kwrite que vim.) Por lo que reiniciar en este momento e
iniciar sesión como root en el entorno gráfico puede ser la mejor&amp;nbsp;idea.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="vincular-al-kernel-correcto"&gt;
&lt;h3&gt;Vincular al kernel&amp;nbsp;correcto&lt;/h3&gt;
&lt;p&gt;Desde la consola, el siguiente&amp;nbsp;comando&lt;/p&gt;
&lt;pre class="literal-block"&gt;
cd /boot ; ls -l
&lt;/pre&gt;
&lt;p&gt;Debería mostrar algo como esto (eliminé algunas líneas&amp;nbsp;innecesarias):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
lrwxrwxrwx  1 root root      35 2008-12-18 00:49 System.map -&amp;gt; System.map-huge-smp-2.6.37.6-smp
-rw-r--r--  1 root root  965704 2008-11-21 02:20 System.map-generic-smp-2.6.37.6-smp
lrwxrwxrwx  1 root root      32 2008-12-18 00:49 vmlinuz -&amp;gt; vmlinuz-huge-smp-2.6.37.6-smp
-rw-r--r--  1 root root 2347888 2008-11-21 02:20 vmlinuz-generic-smp-2.6.37.6-smp
&lt;/pre&gt;
&lt;p&gt;Los archivos System.map y vmlinuz son enlaces que apuntan a las
versiones huge del núcleo (esto puede verse indicado por las flechas -&amp;gt;
junto al nombre del archivo). Debemos vincularlas a las versiones
generic. Para esto, se utilizan los siguientes&amp;nbsp;comandos:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
rm System.map vmlinuz
ln -s vmlinuz-generic-smp-2.6.37.6-smp vmlinuz
ln -s System.map-generic-smp-2.6.37.6-smp System.map
&lt;/pre&gt;
&lt;p&gt;Obviamente conviene hacer uso del autocompletado en &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Bash"&gt;bash&lt;/a&gt;, para no
tener que recordar todo el nombre del archivo (presionar tecla&amp;nbsp;tabulador).&lt;/p&gt;
&lt;p&gt;El siguiente paso es crear el &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Initrd"&gt;archivo initrd&lt;/a&gt; correspondiente. Dado
que el núcleo genérico no tiene los controladores en el núcleo mismo
sino como módulos, es necesario brindarle algunos controladores
iniciales para que pueda encontrar el disco duro, leerlo, y cargar el
resto de los módulos que necesita. Estos controladores iniciales se
guardan en el archivo initrd.
Las instrucciones necesarias para crear este archivo están en el archivo
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;.initrd&lt;/tt&gt; dentro de la carpeta &lt;em&gt;/boot&lt;/em&gt;. Básicamente, para un sistema
cuya partición principal es de tipo &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Ext3"&gt;ext3&lt;/a&gt;, y está en &lt;tt class="docutils literal"&gt;/dev/sda1&lt;/tt&gt; (la
primera partición primaria del primer disco &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Sata"&gt;&lt;span class="caps"&gt;SATA&lt;/span&gt;&lt;/a&gt;), el comando
necesario&amp;nbsp;es&lt;/p&gt;
&lt;pre class="literal-block"&gt;
mkinitrd -c -k 2.6.27.7-smp -m mbcache:jbd:ext3 -f ext3 -r /dev/sda1
&lt;/pre&gt;
&lt;p&gt;(dentro de la carpeta &lt;em&gt;/boot&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;El último paso ahora es configurar &lt;span class="caps"&gt;LILO&lt;/span&gt; para que cargue este archivo
initrd. Habrá que editar el archivo &lt;em&gt;/etc/lilo.conf&lt;/em&gt;, buscar la sección
correspondiente y agregar una línea al&amp;nbsp;bloque&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Linux bootable partition config begins
image = /boot/vmlinuz
root = /dev/sda1
label = Linux
read-only
# Linux bootable partition config ends
&lt;/pre&gt;
&lt;p&gt;que deberá quedar&amp;nbsp;como&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Linux bootable partition config begins
initrd = /boot/initrd.gz
image = /boot/vmlinuz
root = /dev/sda1
label = Linux
read-only
# Linux bootable partition config ends
&lt;/pre&gt;
&lt;p&gt;Ya que estamos editando el archivo &lt;em&gt;lilo.conf&lt;/em&gt;, conviene también editar el
tiempo que se espera antes de arrancar el sistema, en la pantalla de
selección de &lt;a class="reference external" href="http://es.wikipedia.org/wiki/SO"&gt;&lt;span class="caps"&gt;SO&lt;/span&gt;&lt;/a&gt;. Por defecto este tiempo es 2 minutos, recomiendo
cambiarlo a un tiempo que sea corto pero permita interrumpir el proceso
en caso de ser necesario (por ejemplo, medio segundo).
Habrá que ir hacia el principio del archivo &lt;em&gt;lilo.conf&lt;/em&gt; y&amp;nbsp;cambiar:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# This is given in tenths of a second, so 600 for every minute:
timeout = 1200
&lt;/pre&gt;
&lt;p&gt;por&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# This is given in tenths of a second, so 600 for every minute:
timeout = 50
&lt;/pre&gt;
&lt;p&gt;Una vez realizados estos cambios, hay que re-instalar &lt;em&gt;&lt;span class="caps"&gt;LILO&lt;/span&gt;&lt;/em&gt;. Este proceso
es simplemente ejecutar el comando &lt;tt class="docutils literal"&gt;lilo&lt;/tt&gt; en la terminal. Recomiendo
ejecutar primero &lt;tt class="docutils literal"&gt;lilo &lt;span class="pre"&gt;-t&lt;/span&gt;&lt;/tt&gt; (hacer una simulación de la instalación) y
luego, si no hay errores (puede haber advertencias), ejecutar &lt;em&gt;lilo&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ahora, reiniciar el sistema nuevamente para cargar el núcleo&amp;nbsp;genérico.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configurar-el-idioma-predeterminado-del-teclado"&gt;
&lt;h2&gt;Configurar el idioma predeterminado del&amp;nbsp;teclado&lt;/h2&gt;
&lt;p&gt;El idioma del teclado puede configurarse por separado desde los
escritorios (&lt;a class="reference external" href="http://www.kde.org"&gt;&lt;span class="caps"&gt;KDE&lt;/span&gt;&lt;/a&gt; o &lt;a class="reference external" href="http://www.xfce.org"&gt;&lt;span class="caps"&gt;XFCE&lt;/span&gt;&lt;/a&gt;), pero también puede configurarse para el
sistema entero. Por un lado, la terminal de texto tendrá el idioma
configurado durante la instalación, con lo cual no se requieren más
cambios. Pero el servidor X estará por defecto con teclado inglés a
menos que lo configuremos para que utilice&amp;nbsp;otro.&lt;/p&gt;
&lt;p&gt;Esta configuración se debe hacer en un archivo &lt;em&gt;xorg.conf&lt;/em&gt; (dentro de
&lt;em&gt;/etc/X11&lt;/em&gt;). Se debe agregar lo&amp;nbsp;siguiente:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Section &amp;quot;InputClass&amp;quot;
     Identifier     &amp;quot;keyboard-all&amp;quot;
     Driver         &amp;quot;evdev&amp;quot;
     Option         &amp;quot;XkbLayout&amp;quot; &amp;quot;es&amp;quot;
     MatchIsKeyboard &amp;quot;on&amp;quot;
EndSection
&lt;/pre&gt;
&lt;p&gt;No importa si el archivo no contiene nada más (de hecho probablamente
haya que crearlo, ya que inicialmente no es requerido), ya que hoy en
día casi todo es automáticamente detectado por el servidor X. Una vez
agregado esto, el servidor X debería iniciar con el teclado en&amp;nbsp;español.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuracion-de-una-computadora-portatil"&gt;
&lt;h2&gt;Configuración de una computadora&amp;nbsp;portátil&lt;/h2&gt;
&lt;p&gt;Si tienes una computadora de escritorio, no se requieren más detalles de
configuración, todo debería haber sido reconocido y autoconfigurado. El
resto será cuestión de configurar los detalles del entorno gráfico. Pero
si como yo tienes una computadora portátil, habrá otros detalles a&amp;nbsp;configurar.&lt;/p&gt;
&lt;div class="section" id="red-inalambrica"&gt;
&lt;h3&gt;Red&amp;nbsp;inalámbrica&lt;/h3&gt;
&lt;p&gt;Es muy probable que el dispositivo de red inalámbrica no tenga
controladores para Linux. En este caso habrá que apelar a
&lt;a class="reference external" href="http://es.wikipedia.org/wiki/NDISwrapper"&gt;ndiswrapper&lt;/a&gt;, un programa que es capaz de utilizar los controladores
de Windows que estos dispositivos incluyen. ndiswrapper no está incluido
en la distribución de Slackware, pero puede conseguirse sin dificultad
en &lt;a class="reference external" href="http://www.slackbuilds.org"&gt;SlackBuilds.org&lt;/a&gt;.
También se pueden encontrar allí otros controladores nativos para Linux,
por ejemplo el controlador para las tarjetas&amp;nbsp;Broadcom.&lt;/p&gt;
&lt;p&gt;Vale la pena notar, sin embargo, que &lt;em&gt;ndiswrapper&lt;/em&gt; no puede funcionar
correctamente en un sistema de 64bit, no es capaz de cargar un
controlador de 32bit de Windows en un sistema de 64bit. Así que, aún si
tu equipo lo admite, asegúrate de tener los controladores necesarios
antes de instalar&amp;nbsp;Slackware64.&lt;/p&gt;
&lt;p&gt;Para el control de la red inalámbrica Slackware incluye el programa
&lt;a class="reference external" href="http://wicd.sourceforge.net"&gt;wicd&lt;/a&gt; en la carpeta &lt;em&gt;/extra&lt;/em&gt;. Bastará entonces con instalar el paquete
desde esa carpeta. Algo importante a recordar es que, para que wicd
funcione correctamente, es necesario darle permisos de ejecución al
archivo &lt;em&gt;/etc/rc.d/rc.wicd&lt;/em&gt;, y &lt;strong&gt;remover la configuración que se encuentra
en el archivo /etc/rc.d/rc.inet1.conf&lt;/strong&gt;. Para lograr esto simplemente se
agrega una almohadilla # a toda línea de este archivo que no comience
con&amp;nbsp;una.&lt;/p&gt;
&lt;p&gt;Si todo va bien entonces, al reiniciar habrá un ícono de wicd al lado
del reloj, pronto para conectarse a alguna de las redes&amp;nbsp;detectadas.&lt;/p&gt;
&lt;p&gt;En mi computadora el controlador necesario es de Ralink (rt5390sta). Si
bien la empresa ha proveído un controlador abierto, en mi caso no
funcionó bien hasta que lo instalé desde &lt;a class="reference external" href="http://software.opensuse.org/search/download?file=driver:/wireless/11.4-update/src/rt5390sta-2.5.0.3-1.4.src.rpm&amp;amp;query=rt5390sta"&gt;el paquete de openSuSE&lt;/a&gt;
(usando &lt;tt class="docutils literal"&gt;rpm2tgz&lt;/tt&gt;, y compilando con las instrucciones del archivo
&lt;em&gt;.spec&lt;/em&gt;)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="synaptics-el-controlador-para-el-touchpad"&gt;
&lt;h3&gt;Synaptics, el controlador para el&amp;nbsp;touchpad&lt;/h3&gt;
&lt;p&gt;El controlador synaptics viene incluido en Slackware, y el nuevo X.Org
incluido en Slackware 13.37 lo carga correctamente sin que haya que
configurar nada. Pero por defecto desactiva funciones como el hacer clic
tocando la superficie del &lt;em&gt;touchpad&lt;/em&gt;.
Para corregir este comportamiento, se pueden seguir varios caminos. Uno
es editar el archivo &lt;em&gt;xorg.conf&lt;/em&gt;, como se ha hecho toda la vida. El otro
es configurar el touchpad desde el panel de control de &lt;span class="caps"&gt;KDE&lt;/span&gt; (Preferencias
del Sistema). Recomiendo usar esta&amp;nbsp;última.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configuracion-de-ahorro-de-energia"&gt;
&lt;h2&gt;Configuración de ahorro de&amp;nbsp;energía&lt;/h2&gt;
&lt;p&gt;Desde &lt;span class="caps"&gt;KDE4&lt;/span&gt; se pueden configurar sin problemas casi todos los aspectos de
ahorro de energía, excepto por un detalle: qué se hace cuando se
presiona el botón de encendido/apagado (nota: esto era cierto en
versiones anteriores, aún no probé el último &lt;span class="caps"&gt;KDE&lt;/span&gt; en Slackware&amp;nbsp;13.37).&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.linux.com/articles/54610"&gt;En esta página se explica cómo se debe configurar esto&lt;/a&gt;, el artículo
es viejo y la mayor parte de lo que se explica ya no es necesario.
Los pasos para que al presionar el botón la computadora &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Hibernate_(OS_feature)"&gt;hiberne&lt;/a&gt; en
vez de apagarse son los&amp;nbsp;siguientes:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Cambiar a la carpeta &lt;em&gt;/etc/acpi&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Dejar en esta carpeta únicamente el archivo &lt;em&gt;acpi_handler.sh&lt;/em&gt; y crear
las carpetas events and actions en caso de ser&amp;nbsp;necesario.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Crear un archivo &lt;em&gt;/etc/acpi/actions/pwrbtn.sh&lt;/em&gt; con permisos de
ejecución, y con el siguiente&amp;nbsp;contenido:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#!/bin/sh
pm-hibernate
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Crear un archivo &lt;em&gt;/etc/acpi/events/pwrbtn&lt;/em&gt; con el siguiente&amp;nbsp;contenido:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
event=button[ /]power
action=/etc/acpi/actions/pwrbtn.sh
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Listo, al presionar el botón de encendido la computadora hibernará en
vez de apagarse. ¿Pero cómo restaurar?
Lo que hace la hibernación es mover el contenido de la memoria &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Memoria_de_acceso_aleatorio"&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/a&gt; y
de algunas otras cosillas a la partición &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Espacio_de_intercambio"&gt;swap&lt;/a&gt;. Para poder restaurar
el sistema es necesario indicarle al núcleo la partición de swap.
Hay que editar el archivo &lt;em&gt;lilo.conf&lt;/em&gt; de nuevo, buscar la sección de Linux
y añadirle la siguiente&amp;nbsp;línea:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
append = &amp;quot;resume=/dev/sda2&amp;quot;
&lt;/pre&gt;
&lt;p&gt;(donde /dev/sda2 es la partición de swap).
Para finalizar, hay que volver a ejecutar el comando lilo (como antes,
primero &lt;tt class="docutils literal"&gt;lilo &lt;span class="pre"&gt;-t&lt;/span&gt;&lt;/tt&gt; para asegurarse de que todo es correcto, luego &lt;tt class="docutils literal"&gt;lilo&lt;/tt&gt; para
hacer la instalación). Con esto, si la máquina fue hibernada, el núcleo
restaurará el estado. En caso de que la máquina haya sido apagada, se
hará un arranque normal. En mi caso tengo configuradas dos secciones
idénticas, una con el resume y otra sin él, simplemente para hacer
pruebas de vez en&amp;nbsp;cuando.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pasos-siguientes"&gt;
&lt;h2&gt;Pasos&amp;nbsp;siguientes&lt;/h2&gt;
&lt;div class="section" id="instalacion-de-programas"&gt;
&lt;h3&gt;Instalación de&amp;nbsp;programas&lt;/h3&gt;
&lt;p&gt;Slackware no tiene repositorios de paquetes, todos los paquetes
oficiales están en el &lt;span class="caps"&gt;DVD&lt;/span&gt; de instalación. Para instalar programas
externos se recomienda utilizar SlackBuilds, que son scripts que
automatizan la generación de paquetes a partir de archivos de código
fuente. El repositorio más grande de SlackBuilds es &lt;a class="reference external" href="http://www.slackbuilds.org"&gt;slackbuilds.org&lt;/a&gt;,
y la forma más sencilla de utilizar ese repositorio es a través de una
herramienta llamada &lt;a class="reference external" href="http://sbopkg.org/"&gt;sbopkg&lt;/a&gt;.
La mayor parte de los programas tienen su SlackBuild, pero no todos.
Para estos últimos lo mejor es descargar el código fuente y crear un
paquete con &lt;a class="reference external" href="http://www.src2pkg.net/"&gt;src2pkg&lt;/a&gt;. En la mayoría de los casos la compilación será
exitosa. Si no lo es, no queda otra opción más que pedir ayuda en algún
foro para intentar resolver el&amp;nbsp;problema.&lt;/p&gt;
&lt;p&gt;Existen también repositorios extraoficiales de paquetes binarios, siendo
los más grandes &lt;a class="reference external" href="http://linuxpackages.net"&gt;LinuxPackages&lt;/a&gt; y &lt;a class="reference external" href="http://slacky.eu"&gt;Slacky&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gnome"&gt;
&lt;h3&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Por defecto Slackware no incluye el escritorio &lt;a class="reference external" href="http://gnome.org"&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&lt;/a&gt;. Hay varias
formas de incluirlo, la mejor hoy en día es &lt;a class="reference external" href="http://gnomeslackbuild.org/"&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt; SlackBuild&lt;/a&gt;. Incluye
no solamente el escritorio sino también programas relacionados con él,
como Rythmbox, Brasero, Mono y muchos más. Vale la pena tener
disponibles los repositorios para incluir también programas que no son
de &lt;span class="caps"&gt;GNOME&lt;/span&gt; pero Slackware no incluye y &lt;span class="caps"&gt;GSB&lt;/span&gt; sí, como&amp;nbsp;OpenOffice.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="paquetes-casi-oficiales"&gt;
&lt;h3&gt;Paquetes casi&amp;nbsp;oficiales&lt;/h3&gt;
&lt;p&gt;Algunos miembros del equipo de Slackware proveen paquetes adicionales,
no oficiales. El &lt;a class="reference external" href="http://rlworkman.net/pkgs/"&gt;repositorio de rworkman&lt;/a&gt; contiene paquetes extra para
&lt;span class="caps"&gt;XFCE&lt;/span&gt; (y otros), mientras que el &lt;a class="reference external" href="http://alien.slackbook.org/dokuwiki/doku.php"&gt;repositorio de AlienBob&lt;/a&gt; contiene
paquetes como &lt;span class="caps"&gt;VLC&lt;/span&gt;, las últimas versiones de &lt;span class="caps"&gt;KDE&lt;/span&gt;, y lo necesario para
convertir a Slackware64 en un &lt;span class="caps"&gt;SO&lt;/span&gt; capaz de ejecutar programas de 32&amp;nbsp;bit.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="algunos-enlaces-interesantes"&gt;
&lt;h3&gt;Algunos enlaces&amp;nbsp;interesantes:&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.slackware.cl"&gt;SlackwareCL&lt;/a&gt;: comunidad de usuarios de Slackware de Chile, tienen
&lt;a class="reference external" href="http://www.slackware.cl/?q=guia_slackware"&gt;una guía&lt;/a&gt; que puede resultar&amp;nbsp;interesante.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://slacky.eu"&gt;Slacky&lt;/a&gt;: comunidad de usuarios de&amp;nbsp;Italia.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Se supone que esta es solamente una guía básica para tener el sistema
más o menos funcionando. Si algo no termina de quedar bien, recomiendo
el excelente foro de Slackware en &lt;a class="reference external" href="http://www.linuxquestions.org"&gt;LinuxQuestions&lt;/a&gt;, o el no tan grande
pero igualmente útil foro de Sistemas Unix en &lt;a class="reference external" href="http://www.forosdelweb.com"&gt;Foros del&amp;nbsp;Web&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Tue, 27 Sep 2011 23:44:14 -0300</pubDate><guid>tag:blog.elcodiguero.com,2011-09-27:linux-unix/slackware-1337-instalacion-y-configuracion-basica.html</guid><category>Linux &amp; UNIX</category></item><item><title>Copiar y pegar con el ratón en CMD</title><link>http://blog.elcodiguero.com/windows/copiar-y-pegar-con-el-raton-en-cmd.html</link><description>&lt;p&gt;Copiar y pegar en la interfaz de línea de comandos de Windows es un
proceso algo engorroso. Con la configuración predeterminada, hay&amp;nbsp;que:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Hacer un clic con el botón secundario y seleccionar&amp;nbsp;&amp;#8220;Marcar&amp;#8221;&lt;/li&gt;
&lt;li&gt;Seleccionar el texto haciendo clic y moviendo el puntero hacia la
posición&amp;nbsp;deseada&lt;/li&gt;
&lt;li&gt;Presionar el botón derecho para copiar la selección en el&amp;nbsp;portapapeles&lt;/li&gt;
&lt;li&gt;Seleccionar &amp;#8220;Pegar&amp;#8221; en el menú&amp;nbsp;contextual&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Es bastante molesto, al menos bastante más molesto que el proceso normal
de copiar y pegar en otro programa de Windows. Pero no tiene por qué ser
así, Windows ofrece una configuración que resultará familiar a quienes
sean usuarios de &lt;a class="reference external" href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"&gt;PuTTY&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Primero hay que ir a una ventana cualquiera de &lt;span class="caps"&gt;CMD&lt;/span&gt;, por ejemplo vía
&lt;em&gt;Inicio&lt;/em&gt;, &lt;em&gt;Ejecutar&lt;/em&gt;, &lt;em&gt;cmd&lt;/em&gt;.
Luego hay que hacer clic en el ícono de la barra de título (a la
izquierda) o bien presionar &lt;em&gt;Alt+Espacio&lt;/em&gt;.
En el menú que aparece, seleccionar &lt;strong&gt;Predeterminados&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Aparece la ventana &lt;em&gt;Propiedades de Consola de Windows&lt;/em&gt;, como en la
imagen&amp;nbsp;siguiente.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Ventana de propiedades de CMD" src="/imagenes/cmd1.png" /&gt;
&lt;/div&gt;
&lt;p&gt;Basta seleccionar &lt;em&gt;Modalidad de edición rápida&lt;/em&gt; en el cuadro &lt;em&gt;Opciones
de edición&lt;/em&gt;, aceptar y salir.
Este cambio habilita selección de texto normal en la consola (arrastrar
el ratón sobre el texto), copia con el botón derecho, y pegado con el
botón derecho. Mucho más rápido y práctico que&amp;nbsp;antes.&lt;/p&gt;
&lt;p&gt;La ventana &lt;em&gt;Propiedades de Consola de Windows&lt;/em&gt; también permite cambiar
la tipografía (fuente) de la consola y su tamaño, algo interesante para
todos aquellos a quienes alguna vez nos molestó que la consola no se
pueda agrandar a lo&amp;nbsp;ancho.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sun, 17 Apr 2011 16:14:26 -0300</pubDate><guid>tag:blog.elcodiguero.com,2011-04-17:windows/copiar-y-pegar-con-el-raton-en-cmd.html</guid><category>Windows</category></item><item><title>Eliminar objetos repetidos de una lista</title><link>http://blog.elcodiguero.com/python/eliminar-objetos-repetidos-de-una-lista.html</link><description>&lt;p&gt;En Python las listas y tuplas permiten elementos duplicados. Cuando se
quiere manejar elementos únicos, se recurre normalmente a conjuntos
(&lt;a class="reference external" href="http://docs.python.org/library/stdtypes.html#set-types-set-frozenset"&gt;set&lt;/a&gt;), o a &lt;a class="reference external" href="http://docs.python.org/library/stdtypes.html#mapping-types-dict"&gt;diccionarios&lt;/a&gt; (donde lo que debe ser único es cada
clave, no hay restricciones en el&amp;nbsp;contenido).&lt;/p&gt;
&lt;p&gt;Pero si se recibe una lista con duplicados y queremos eliminarlos, ¿cuál
es la forma más&amp;nbsp;sencilla?&lt;/p&gt;
&lt;div class="section" id="con-un-bucle-for"&gt;
&lt;h2&gt;Con un bucle&amp;nbsp;for&lt;/h2&gt;
&lt;p&gt;Si no se conoce de antemano un método, uno podría pensar en resolver el
problema recorriendo la lista, y construyendo una segunda lista
solamente con los elementos que no pertenezcan ya a ella, algo&amp;nbsp;así:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="n"&gt;lista_nueva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lista_original&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lista_nueva&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;        &lt;span class="n"&gt;lista_nueva&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Si bien esta solución es simple, vale notar que es &lt;em&gt;O(n^2)&lt;/em&gt; peor caso
(cuando no hay duplicados), ya que por cada elemento de la lista
original se debe recorrer potencialmente toda la lista nueva. Como
ventaja se puede mencionar que preserva el orden de la lista&amp;nbsp;original.&lt;/p&gt;
&lt;p&gt;Pero vamos, ¡esto es Python! ¡Debe haber un método mejor!&amp;nbsp;:-)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="con-un-diccionario"&gt;
&lt;h2&gt;Con un&amp;nbsp;diccionario&lt;/h2&gt;
&lt;p&gt;Aprovechando que los diccionarios no pueden contener claves duplicadas,
se puede crear un diccionario tal que cada una de sus claves es un
elemento de la lista. Luego, con el método &lt;tt class="docutils literal"&gt;keys&lt;/tt&gt; se obtiene una lista
de las&amp;nbsp;claves.&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromkeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista_original&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Este método es &lt;em&gt;O(n)&lt;/em&gt;, pero tiene la desventaja de que no preserva el
orden de los elementos de la lista&amp;nbsp;original.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="usando-conjuntos"&gt;
&lt;h2&gt;Usando&amp;nbsp;conjuntos&lt;/h2&gt;
&lt;p&gt;Este método es muy similar al anterior, de hecho tiene tiene la misma
ventaja (es &lt;em&gt;O(n)&lt;/em&gt;) y la misma desventaja (no preserva el orden de la
lista original). Se trata de crear un conjunto a partir de la lista, y
luego (en caso de requerirlo) volver a convertir el conjunto a una
lista. Los conjuntos no pueden contener elementos repetidos, por lo que
se eliminan los&amp;nbsp;duplicados.&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista_original&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="usando-comprension-de-listas"&gt;
&lt;h2&gt;Usando comprensión de&amp;nbsp;listas&lt;/h2&gt;
&lt;p&gt;Este es un ejemplo útil y eficiente aunque complejo, que encontré en
&lt;a class="reference external" href="http://bytes.com/topic/python/answers/24382-delete-duplicates-list"&gt;bytes.com&lt;/a&gt;. La idea es utilizar las propiedades de los diccionarios
para eliminar los elementos&amp;nbsp;duplicados.&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lista_original&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Para cada elemento de la lista original, si el elemento no es una clave
del diccionario (&lt;tt class="docutils literal"&gt;x not in d&lt;/tt&gt;) se agrega al diccionario via
&lt;a class="reference external" href="http://docs.python.org/library/stdtypes.html#dict.setdefault"&gt;setdefault&lt;/a&gt;. Este método tiene la particularidad de que si la clave no
existe, la agrega (por lo que no se la volverá a agregar gracias al
&lt;tt class="docutils literal"&gt;x not in d&lt;/tt&gt;) y devuelve el valor que se le pasó como segundo
parámetro, generando efectivamente una lista de elementos únicos y
preservando el orden&amp;nbsp;original.&lt;/p&gt;
&lt;p&gt;Como los diccionarios (también los conjuntos) son &lt;em&gt;O(1)&lt;/em&gt; caso promedio
para la búsqueda de elementos (por estar implementados como &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Tabla_hash"&gt;tablas
hash&lt;/a&gt;), el costo de este método es &lt;em&gt;O(n)&lt;/em&gt;, por el recorrido de los
elementos de la&amp;nbsp;lista.&lt;/p&gt;
&lt;p&gt;En general preferiría usar conjuntos, ya que es la forma más sencilla.
Pero si hay que preservar el orden de la lista original, sin duda este
es un buen método, y no es tan complejo como parece a primera&amp;nbsp;vista.&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://docs.python.org/library/stdtypes.html#set-types-set-frozenset"&gt;Python Built-in Types:&amp;nbsp;set&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://docs.python.org/library/stdtypes.html#mapping-types-dict"&gt;Python Built-in Types:&amp;nbsp;dict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Tabla_hash"&gt;Wikipedia: Tabla&amp;nbsp;Hash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sun, 30 Jan 2011 16:27:18 -0200</pubDate><guid>tag:blog.elcodiguero.com,2011-01-30:python/eliminar-objetos-repetidos-de-una-lista.html</guid><category>Python</category></item><item><title>Comentar bloque de código con ViM</title><link>http://blog.elcodiguero.com/linux-unix/comentar-bloque-de-codigo-con-vim.html</link><description>&lt;p&gt;ViM es uno de los editores más conocidos en el mundo &lt;span class="caps"&gt;UNIX&lt;/span&gt;, y sin duda
alguna es uno de los más completos y potentes una vez que se lo conoce
bien. Es posible usarlo durante años y seguir aprendiendo formas en las
que nos ahorra&amp;nbsp;trabajo.&lt;/p&gt;
&lt;div class="section" id="como-comentar-un-bloque-de-codigo"&gt;
&lt;h2&gt;Cómo comentar un bloque de&amp;nbsp;código&lt;/h2&gt;
&lt;p&gt;Me refiero a, en general, agregar un caracter cualquiera al principio de
varias líneas, sin tener que editar línea por línea.
Para esto, hay que posarse en la primera columna de la línea, cambiar a
modo comando (con &lt;em&gt;Esc&lt;/em&gt;) y presionar &lt;em&gt;Ctrl+V&lt;/em&gt; para ingresar en el modo
&lt;em&gt;Bloque visual&lt;/em&gt;, que se indicará en la parte inferior de la pantalla
como &lt;strong&gt;&amp;#8212; &lt;span class="caps"&gt;VISUAL&lt;/span&gt; &lt;span class="caps"&gt;BLOCK&lt;/span&gt; &amp;#8212;&lt;/strong&gt; o &lt;strong&gt;&amp;#8212; &lt;span class="caps"&gt;BLOQUE&lt;/span&gt; &lt;span class="caps"&gt;VISUAL&lt;/span&gt; &amp;#8212;&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Pantalla de VIM con texto" src="/imagenes/vim_1.gif" /&gt;
&lt;/div&gt;
&lt;p&gt;Una vez en este modo, nos desplazamos hacia abajo (o hacia arriba) con
las flechas del teclado, cubriendo todas las líneas que queramos&amp;nbsp;comentar.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Pantalla de VIM en modo Bloque Visual, con texto seleccionado" src="/imagenes/vim_2.gif" /&gt;
&lt;/div&gt;
&lt;p&gt;El tercer paso es presionar &lt;em&gt;I&lt;/em&gt; (i mayúscula) y escribir el texto que deba
ir al principio de la línea (por ejemplo, &lt;em&gt;#&lt;/em&gt; o &lt;em&gt;//&lt;/em&gt;). Esto agregará el
texto a la línea en la que se ubique el&amp;nbsp;cursor.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Pantalla de VIM, en modo Inserción, agregando texto al principio de una línea" src="/imagenes/vim_3.gif" /&gt;
&lt;/div&gt;
&lt;p&gt;Al presionar la tecla &lt;em&gt;Esc&lt;/em&gt; se modificarán todas las líneas marcadas.
Dependiendo de la versión de ViM, puede ser necesario esperar unos
segundos o mover el cursor para ver el&amp;nbsp;cambio.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Pantalla de VIM, con todas las líneas modificadas como la primera." src="/imagenes/vim_4.gif" /&gt;
&lt;/div&gt;
&lt;p&gt;Para borrar los caracteres (&lt;em&gt;descomentar&lt;/em&gt;) se sigue un proceso similar,
pero en vez de escribir, una vez seleccionadas las líneas hay que
apretar &lt;strong&gt;d&lt;/strong&gt; para borrar el o los caracteres que se quieran quitar de cada&amp;nbsp;línea.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 20 Jan 2011 21:21:16 -0200</pubDate><guid>tag:blog.elcodiguero.com,2011-01-20:linux-unix/comentar-bloque-de-codigo-con-vim.html</guid><category>Linux &amp; UNIX</category></item><item><title>Sitios web con Python, virtualenv y fastcgi</title><link>http://blog.elcodiguero.com/python/sitios-web-con-python-virtualenv-y-fastcgi.html</link><description>&lt;p&gt;A la hora de crear un sitio web con Python, la opción más sencilla es
usar un &lt;em&gt;framework&lt;/em&gt; y algunas de las muchas aplicaciones y bibliotecas
ya disponibles. Pero ¿qué sucede si usamos una biblioteca que no está
disponible en la cuenta de alojamiento que&amp;nbsp;contratamos?&lt;/p&gt;
&lt;div class="section" id="virtualenv"&gt;
&lt;h2&gt;virtualenv&lt;/h2&gt;
&lt;p&gt;La respuesta a esta pregunta es &lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;virtualenv&lt;/a&gt;. Se trata de un paquete
que permite instalar otros paquetes y bibliotecas en un directorio
cualquiera, sin depender de la carpeta global de Python o heredando los
paquetes instalados en&amp;nbsp;ella.&lt;/p&gt;
&lt;p&gt;En mi cuenta de &lt;a class="reference external" href="http://secure.valcato.com/aff.php?aff=064"&gt;Valcato&lt;/a&gt; tuve que utilizar &lt;em&gt;virtualenv&lt;/em&gt; para instalar
&lt;a class="reference external" href="http://www.django-cms.org/"&gt;django-cms&lt;/a&gt; y el blog &lt;a class="reference external" href="http://django-blog-zinnia.com"&gt;Zinnia&lt;/a&gt;, ya que no estaban entre los paquetes&amp;nbsp;disponibles.&lt;/p&gt;
&lt;p&gt;En mi caso, &lt;em&gt;virtualenv&lt;/em&gt; no estaba instalado en la carpeta global, pero
esto no es un problema: basta con descargar el archivo .zip y
descomprimirlo en una carpeta cualquiera del servidor. Luego simplemente
ejecutando &lt;tt class="docutils literal"&gt;python virtualenv.py&lt;/tt&gt; funciona&amp;nbsp;perfectamente.&lt;/p&gt;
&lt;div class="section" id="crear-el-entorno-virtual"&gt;
&lt;h3&gt;Crear el entorno&amp;nbsp;virtual&lt;/h3&gt;
&lt;p&gt;Para crear el entorno virtual, debemos conectarnos a la cuenta de
alojamiento vía &lt;span class="caps"&gt;SSH&lt;/span&gt;, usando &lt;a class="reference external" href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"&gt;PuTTY&lt;/a&gt; o un programa similar. Esto es
necesario porque se requiere ejecutar comandos.
Una vez conectados y en la línea de comandos del servidor, basta con
ejecutar el comando &lt;tt class="docutils literal"&gt;virtualenv&lt;/tt&gt; con un parámetro que indica el nombre
de la carpeta en la que se copiarán los archivos&amp;nbsp;necesarios:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# virtualenv mi_entorno_virtual
&lt;/pre&gt;
&lt;p&gt;Para evitar problemas, conviene tener dos puntos en&amp;nbsp;consideración:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;El entorno virtual no puede ser cambiado de directorio, por lo que
hay que crearlo en la carpeta desde donde vaya a funcionar&amp;nbsp;siempre.&lt;/li&gt;
&lt;li&gt;Existe una opción &lt;em&gt;&amp;#8212;no-site-packages&lt;/em&gt; que desvincula completamente
al entorno virtual de la carpeta global. Ideal si queremos evitar
cualquier clase de&amp;nbsp;conflicto.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="activar-el-entorno"&gt;
&lt;h3&gt;Activar el&amp;nbsp;entorno&lt;/h3&gt;
&lt;p&gt;Luego de crearlo, y antes de poder utilizarlo, hay que &lt;em&gt;activar&lt;/em&gt; el
entorno virtual recién creado. Esto hace que las siguientes llamadas a
python o a programas hechos en python encuentren las carpetas del
entorno virtual y sea posible instalar paquetes allí. La activación
requiere otro comando&amp;nbsp;simple:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# . mi_entorno_virtual/bin/activate
&lt;/pre&gt;
&lt;p&gt;Es importante ejecutar el comando anterior tal cual se muestra,
incluyendo el punto inicial (el # es simplemente una forma genérica de
representar el indicador de la línea de comandos o &lt;em&gt;prompt&lt;/em&gt;). Obviamente
cambia el nombre de la carpeta donde el entorno virtual fue creado. Lo
importante es que luego de esto, las llamadas a python ejecutarán
&lt;tt class="docutils literal"&gt;mi_entorno_virtual/bin/python&lt;/tt&gt;, y
&lt;em&gt;mi_entorno_virtual/lib/python/site-packages&lt;/em&gt; estará en &lt;tt class="docutils literal"&gt;sys.path&lt;/tt&gt; (de
forma simple: lo que esté en &lt;em&gt;mi_entorno_virtual&lt;/em&gt; tendrá precedencia
sobre lo que esté en la carpeta global de Python en el&amp;nbsp;sistema)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="instalando-programas-y-bibliotecas"&gt;
&lt;h3&gt;Instalando programas y&amp;nbsp;bibliotecas&lt;/h3&gt;
&lt;p&gt;Una vez activado el entorno virtual, se dispone de los comandos &lt;tt class="docutils literal"&gt;pip&lt;/tt&gt;
y &lt;tt class="docutils literal"&gt;easy_install&lt;/tt&gt; para instalar paquetes de python en él, sin afectar
lo que esté instalado en el directorio global. En mi caso necesitaba
instalar &lt;em&gt;django-blog-zinnia&lt;/em&gt;, así que simplemente usé la siguiente&amp;nbsp;orden:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# pip install django-blog-zinnia
&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;pip&lt;/em&gt; no solamente instala el paquete indicado sino que instala también
sus dependencias, por lo que una vez finalizada su tarea ya queda todo
listo para&amp;nbsp;usar.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="modificando-la-configuracion-de-fastcgi"&gt;
&lt;h2&gt;Modificando la configuración de&amp;nbsp;FastCGI&lt;/h2&gt;
&lt;p&gt;Para poder usar el entorno virtual desde la línea de comandos, se
requiere ejecutar el programa &lt;tt class="docutils literal"&gt;bin/activate&lt;/tt&gt;, pero FastCGI no tiene
una línea de comandos. Entonces ¿cómo indicarle que utilice los paquetes
allí instalados? La respuesta está en la biblioteca estándar de Python,
y es el módulo &lt;tt class="docutils literal"&gt;site&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="section" id="site-addsitedir"&gt;
&lt;h3&gt;site.addsitedir&lt;/h3&gt;
&lt;p&gt;El método addsitedir del módulo &lt;tt class="docutils literal"&gt;site&lt;/tt&gt; sirve para lo que necesitamos:
agregar una nueva carpeta al entorno de ejecución de Python para que sea
considerada como otra &lt;em&gt;site-packages&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Si ya estás usando &lt;a class="reference external" href="http://www.django-project.com"&gt;Django&lt;/a&gt; con FastCGI, seguramente tengas un archivo
con extensión .fcgi y con contenido similar a lo&amp;nbsp;siguiente:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="c"&gt;#!/usr/bin/env python2.6&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/ruta/a/tu/proyecto/django&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt;5 &lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'DJANGO_SETTINGS_MODULE'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;proyecto.settings&amp;quot;&lt;/span&gt;
&lt;span class="ln"&gt;6 &lt;/span&gt;
&lt;span class="ln"&gt;7 &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.servers.fastcgi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;runfastcgi&lt;/span&gt;
&lt;span class="ln"&gt;8 &lt;/span&gt;&lt;span class="n"&gt;runfastcgi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;threaded&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;daemonize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Entonces, para que FastCGI encuentre y use los paquetes instalados en el
entorno virtual, se importa el módulo y se invoca a &lt;tt class="docutils literal"&gt;addsitedir&lt;/tt&gt; antes
de hacer el &lt;tt class="docutils literal"&gt;sys.path.insert&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;site&lt;/span&gt;
&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addsitedir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/ruta/al/entorno/virtual/lib/python2.6/site-packages'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="conflictos-con-el-site-packages-global"&gt;
&lt;h3&gt;Conflictos con el site-packages&amp;nbsp;global&lt;/h3&gt;
&lt;p&gt;Si no hay conflictos de paquetes entre el directorio global y el entorno
virtual, lo anterior es suficiente para que todo funcione correctamente.
Sin embargo, en caso de conflictos pueden surgir problemas. Esto es
porque &lt;tt class="docutils literal"&gt;addsitedir&lt;/tt&gt; agrega los directorios del entorno virtual
&lt;strong&gt;después&lt;/strong&gt; de los directorios globales en &lt;tt class="docutils literal"&gt;sys.path&lt;/tt&gt;, y sin un orden
predefinido. Si tu archivo &lt;tt class="docutils literal"&gt;.fcgi&lt;/tt&gt; utiliza &lt;tt class="docutils literal"&gt;addsitedir&lt;/tt&gt; y un día de
repente algo no funciona, o funciona mal, podría ser producto de un
conflicto de versiones del mismo paquete entre el entorno virtual y el
entorno global del&amp;nbsp;servidor.&lt;/p&gt;
&lt;p&gt;El problema está más detallado &lt;a class="reference external" href="http://code.google.com/p/modwsgi/wiki/VirtualEnvironments"&gt;en el sitio de mod_wsgi&lt;/a&gt;. La solución
es básicamente reordenar &lt;tt class="docutils literal"&gt;sys.path&lt;/tt&gt; para que los directorios agregados por
&lt;tt class="docutils literal"&gt;addsitedir&lt;/tt&gt; aparezcan primero. El resultado final en el archivo de
FastCGI es algo como&amp;nbsp;esto:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="c"&gt;#!/usr/bin/env python2.6&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;site&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="c"&gt;# Remember original sys.path.&lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="n"&gt;prev_sys_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt;
&lt;span class="ln"&gt; 6 &lt;/span&gt;
&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="c"&gt;# Add each new site-packages directory.&lt;/span&gt;
&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addsitedir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/ruta_al_entorno_virtual/elcodiguero-env/lib/python2.6/site-packages'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt; 9 &lt;/span&gt;
&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="c"&gt;# Reorder sys.path so new directories at the front.&lt;/span&gt;
&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="n"&gt;new_sys_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="ln"&gt;13 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prev_sys_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="ln"&gt;14 &lt;/span&gt;        &lt;span class="n"&gt;new_sys_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt;15 &lt;/span&gt;        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_sys_path&lt;/span&gt;
&lt;span class="ln"&gt;17 &lt;/span&gt;
&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/ruta_al/proyecto_django/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'DJANGO_SETTINGS_MODULE'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;proyecto.settings&amp;quot;&lt;/span&gt;
&lt;span class="ln"&gt;20 &lt;/span&gt;
&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.servers.fastcgi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;runfastcgi&lt;/span&gt;
&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="n"&gt;runfastcgi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;threaded&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;daemonize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Una vez cumplidos estos pasos, nuestro sitio Django tendrá disponibles
todos los paquetes que queramos instalar en el entorno&amp;nbsp;virtual.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 30 Dec 2010 19:49:44 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-12-30:python/sitios-web-con-python-virtualenv-y-fastcgi.html</guid><category>Python</category></item><item><title>Definición de funciones en Javascript</title><link>http://blog.elcodiguero.com/javascript/definicion-de-funciones-en-javascript.html</link><description>&lt;p&gt;Las funciones son un elemento vital en cualquier lenguaje de
programación. Pero no todos los lenguajes las manejan de la misma forma;
en muchos son tipos de datos especiales que solamente pueden definirse y
luego llamarse con (o sin) parámetros, pero otros como Javascript las
tratan como simples variables que pueden ser llamadas. A continuación
intento describir de forma sencilla lo que se puede hacer con las
funciones en&amp;nbsp;Javascript.&lt;/p&gt;
&lt;div class="section" id="funciones-con-nombre-y-funciones-anonimas"&gt;
&lt;h2&gt;Funciones con nombre y funciones&amp;nbsp;anónimas&lt;/h2&gt;
&lt;p&gt;En todos los lenguajes se pueden declarar funciones con nombre, en
javascript esto es tan simple como&amp;nbsp;hacer:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;mi_funcion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parametros&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Esto crea una función de nombre &lt;em&gt;mi_funcion&lt;/em&gt;, que luego puede llamarse
con los parámetros&amp;nbsp;adecuados.&lt;/p&gt;
&lt;p&gt;Algunos lenguajes, además, permiten declarar &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Anonymous_function"&gt;funciones anónimas&lt;/a&gt;:
funciones que no tienen un nombre y que sirven para esas ocasiones en
las que no se quiere crear una función para usarla solamente una vez. En
javascript una situación como esta puede ser crear una función que
responda a un&amp;nbsp;evento:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;El código anterior crea una función sin nombre que se asigna al objeto
&lt;em&gt;onload&lt;/em&gt;, y que puede ejecutarse luego haciendo una llamada a
&lt;tt class="docutils literal"&gt;window.onload()&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="metodos-de-objetos"&gt;
&lt;h2&gt;Métodos de&amp;nbsp;objetos&lt;/h2&gt;
&lt;p&gt;Las funciones anónimas se usan también para agregar &lt;em&gt;métodos&lt;/em&gt; a objetos,
de hecho es la única forma de hacerlo ya que Javascript no posee una
construcción del lenguaje para crear&amp;nbsp;clases.&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;miobjeto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;un_metodo&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parametros&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;En este caso miobjeto posee un método un_metodo, que al llamarse como
&lt;tt class="docutils literal"&gt;miobjeto.un_metodo(parametros)&lt;/tt&gt; ejecuta la función anónima. Esto es
posible gracias a que en Javascript las variables son un &lt;em&gt;`tipo de dato
de primera&amp;nbsp;clase`_&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tipo-de-dato-de-primera-clase"&gt;
&lt;h2&gt;Tipo de dato de primera&amp;nbsp;clase&lt;/h2&gt;
&lt;p&gt;Se puede encontrar una descripción en Wikipedia de lo que significa ser
un tipo de dato de primera clase, pero puesto en términos sencillos
quiere decir que las funciones son datos como cualquier otro y que
pueden por tanto asignarse, crearse, pasarse como parámetro y
manipularse casi de cualquier forma. Así, en Javascript suelen pasarse
objetos de función a algunas construcciones comunes del lenguaje, por&amp;nbsp;ejemplo:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;mi_funcion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;    &lt;span class="cm"&gt;/* código */&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;
&lt;span class="ln"&gt;5 &lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mi_funcion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;6 &lt;/span&gt;&lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mi_funcion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;7 &lt;/span&gt;&lt;span class="nx"&gt;algun_elemento&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mi_funcion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Anonymous_function"&gt;Wikipedia: función&amp;nbsp;anónima&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/First-class_object"&gt;Wikipedia: Objeto de primera&amp;nbsp;clase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Javascript"&gt;Wikipedia:&amp;nbsp;Javascript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sun, 01 Aug 2010 00:19:56 -0300</pubDate><guid>tag:blog.elcodiguero.com,2010-08-01:javascript/definicion-de-funciones-en-javascript.html</guid><category>Javascript</category></item><item><title>¿Javascript, o «script de Java»?</title><link>http://blog.elcodiguero.com/javascript/javascript-o-script-de-java.html</link><description>&lt;p&gt;He escuchado demasiadas veces la frase script de Java referida a
&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Javascript"&gt;Javascript&lt;/a&gt;. Probablemente se trate de una mala traducción que
desafortunadamente fue considerada correcta, pero la verdad es que
escucharla de un ingeniero en sistemas me choca, y mucho. Saber que
gente que sabe piensa que Javascript es simplemente un subconjunto de
&lt;a class="reference external" href="http://www.java.com"&gt;Java&lt;/a&gt; es muy frustrante. Por eso es mi intención ahora mismo nombrar
alguna de las diferencias que se me vienen a la mente, simplemente para
tenerlo como referencia la próxima vez que me toque explicarle a alguien
por qué Javascript &lt;span class="caps"&gt;NO&lt;/span&gt; significa script de&amp;nbsp;Java.&lt;/p&gt;
&lt;p&gt;Exponer las diferencias entre Java y Javascript se ha hecho muchas veces
antes, pero tengo la sensación de que la mayoría de las veces que se
escribió sobre el tema se hizo siendo Javascript un lenguaje despreciado
y considerado casi un juguetito de las páginas web. Javascript es mucho
más que un Java básico y con algunas cosas raras, e intentaré hacer
valer eso en este&amp;nbsp;artículo.&lt;/p&gt;
&lt;div class="section" id="historia"&gt;
&lt;h2&gt;Historia&lt;/h2&gt;
&lt;p&gt;Dejaré que Wikipedia mencione los detalles sobre la &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Javascript#Historia_y_denominaci.C3.B3n"&gt;historia del
lenguaje&lt;/a&gt;. Lo principal en esta historia es que el lenguaje que hoy
conocemos como Javascript comenzó su vida llamándose LiveScript: el
cambio de nombre fue únicamente (según algunos) para aprovechar la
publicidad que se hacía en la época a Java y el impulso comercial que
éste estaba&amp;nbsp;teniendo.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ejecucion"&gt;
&lt;h2&gt;Ejecución&lt;/h2&gt;
&lt;p&gt;Partiendo en ambos lenguajes de un código fuente válido, el proceso que
sigue un programa en Java es bastante diferente al que sigue un programa
en&amp;nbsp;Javascript.&lt;/p&gt;
&lt;div class="section" id="java"&gt;
&lt;h3&gt;Java&lt;/h3&gt;
&lt;p&gt;Un programa Java se compila, generando un archivo .class por cada clase
o un &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Jar"&gt;&lt;span class="caps"&gt;JAR&lt;/span&gt;&lt;/a&gt; que incluye todas las clases.
Es la &lt;a class="reference external" href="http://es.wikipedia.org/wiki/JVM"&gt;&lt;span class="caps"&gt;JVM&lt;/span&gt;&lt;/a&gt; (Máquina Virtual de Java) la que luego ejecuta el&amp;nbsp;programa.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="javascript-en-un-navegador"&gt;
&lt;h3&gt;Javascript (en un&amp;nbsp;navegador)&lt;/h3&gt;
&lt;p&gt;El código fuente Javascript se incluye en la página &lt;a class="reference external" href="http://es.wikipedia.org/wiki/HTML"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt;&lt;/a&gt; mediante una
etiqueta &lt;tt class="docutils literal"&gt;&amp;lt;script&amp;gt;&lt;/tt&gt; o &lt;tt class="docutils literal"&gt;&amp;lt;link&amp;gt;&lt;/tt&gt; en el caso de un archivo externo. El
navegador interpreta el código y lo ejecuta en un entorno restringido a
la página desde la cual es&amp;nbsp;vinculado.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="sintaxis"&gt;
&lt;h2&gt;Sintaxis&lt;/h2&gt;
&lt;p&gt;Tanto Java como Javascript utilizan una sintaxis basada en la de C.
Se podría decir que Java se parece más a C++, mientras que Javascript se
parece más a&amp;nbsp;C.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tipos-de-datos"&gt;
&lt;h2&gt;Tipos de&amp;nbsp;datos&lt;/h2&gt;
&lt;p&gt;Java incluye los &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Tipo_de_dato"&gt;tipos de datos&lt;/a&gt; básicos integer (y derivados),
String, boolean, float y double. Además, cada clase es un tipo de dato
con el que podemos crear variables. Una variable de un tipo no puede
redefinirse como de otro (a menos que otro sea superclase de uno), y se
aplican comprobaciones estrictas de tipos. A esto se le llama &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Lenguaje_de_programación_fuertemente_tipado"&gt;tipado
estático&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Javascript solamente posee los tipos de datos number, object, string,
function. Las variables se definen sin tipo, serán del tipo que sea
aquello que se les está asignando. A una variable definida de cierto
tipo se le puede asignar un valor de otro tipo (por ejemplo, a una
variable entera puede asignársele una cadena), y el tipo de dato de la
variable cambia. A esto se le llama &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Tipado_dinámico"&gt;tipado dinámico&lt;/a&gt;.
Veamos un par de ejemplos para ver la diferencia. Primero, código&amp;nbsp;Java:&lt;/p&gt;
&lt;pre class="code java literal-block"&gt;
&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Hola&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// variable se declara de tipo String
&lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// dará un error de compilación, no se puede asignar un entero.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Y ahora un código equivalente en&amp;nbsp;Javascript:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hola&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// variable se declara sin tipo. Como se le asigna una cadena, tendrá el tipo String
&lt;/span&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// variable es ahora de tipo entero. No hay errores
&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="ambito-de-las-variables"&gt;
&lt;h2&gt;Ámbito de las&amp;nbsp;variables&lt;/h2&gt;
&lt;p&gt;Java, al igual que la mayoría de los lenguajes basados en C, tiene
variables de bloque. Es decir, que una variable definida en un bloque de
código (delimitado por las llaves { y }) existirá solamente dentro de
ese bloque. En caso de producirse ambigüedad (una variable local al
bloque se llama igual que una variable externa) tiene prioridad el
espacio de nombres&amp;nbsp;local.&lt;/p&gt;
&lt;p&gt;En Javascript, por otra parte, solamente las funciones generan su propio
espacio de nombres. Los bloques de código utilizan las variables
definidas fuera, y si definen alguna variable ésta no desaparecerá
cuando el programa salga del bloque, sino que seguirá definida en el
espacio de nombres global (o de la función en la que esté el bloque).
Esto es algo bastante molesto en realidad, ya que puede fácilmente
generar conflictos y es una de las principales razones (por si no
hubiera ya suficientes) para evitar las variables&amp;nbsp;globales.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="funcional"&gt;
&lt;h2&gt;Funcional&lt;/h2&gt;
&lt;p&gt;En java las funciones &lt;span class="caps"&gt;NO&lt;/span&gt; son un tipo de datos. En Javascript sí: una
función es una variable como cualquier otra, que se puede pasar como
parámetro a otra función o asignar a una variable. Incluso se pueden
declarar funciones anónimas para utilizarlas una sola vez y que no
ocupen el espacio de nombres. Se dice que una función es un &lt;a class="reference external" href="http://en.wikipedia.org/wiki/First-class_data_type"&gt;tipo de
dato de primera clase&lt;/a&gt;. Esto, junto con otras características, hacen
que javascript pueda considerarse un lenguaje funcional, y, en palabras
del experto &lt;a class="reference external" href="http://www.crockford.com"&gt;Douglas Crockford&lt;/a&gt;, el primer lenguaje lambda en llegar a
utilizarse masivamente. (donde &lt;em&gt;lambda&lt;/em&gt; proviene del &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Cálculo_lambda"&gt;cálculo lambda&lt;/a&gt;,
y es otro nombre para una función&amp;nbsp;anónima.)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="orientacion-a-objetos"&gt;
&lt;h2&gt;Orientación a&amp;nbsp;Objetos&lt;/h2&gt;
&lt;p&gt;Java es orientado a objetos, como todos sabemos, y utiliza una
orientación a objetos &lt;em&gt;clásica&lt;/em&gt;, es decir, basada en clases. Los objetos
son &lt;em&gt;ejemplares&lt;/em&gt; de una clase (disculpen pero detesto utilizar
&lt;em&gt;instancia&lt;/em&gt; como traducción de &lt;em&gt;instance&lt;/em&gt;), y la herencia se da entre&amp;nbsp;clases.&lt;/p&gt;
&lt;p&gt;Javascript es también orientado a objetos, pero no usa clases, aunque
hay formas de simularlas y se utiliza algo similar a una clase al crear
un objeto de tipo &lt;strong&gt;Date&lt;/strong&gt;.
En Javascript la herencia se da directamente entre objetos, mediante
&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Programación_basada_en_prototipos"&gt;prototipos&lt;/a&gt;. Todo objeto contiene un atributo oculto que lo vincula a
su objeto padre, formando una cadena hasta el objeto padre &lt;strong&gt;Object&lt;/strong&gt;
(que en los navegadores es&amp;nbsp;window).&lt;/p&gt;
&lt;p&gt;Un objeto hereda todos los métodos y atributos de su objeto padre, y si
al objeto padre se le agrega un método o atributo, los objetos hijos
podrán ver inmediatamente el nuevo miembro añadido. Todo objeto contiene
un atributo prototype al que se puede agregar miembros (atributos o
métodos) para que todos los objetos hijos puedan verlos. Por ejemplo, si
agrega un método metodo a &lt;em&gt;String.prototype&lt;/em&gt;, todas las cadenas
contendrán el&amp;nbsp;método.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Espero que este repaso por algunas de las principales diferencias entre
Java y Javascript sirva de algo, con un poco de suerte estaré ayudando a
que ya nadie piense en Javascript como un hermanito menor de Java. Para
aquellos que quieran profundizar en el lenguaje, recomiendo los videos
disponibles en el &lt;a class="reference external" href="http://developer.yahoo.com/yui/theater/"&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt; Theater&lt;/a&gt; y el libro &lt;a class="reference external" href="http://www.amazon.com/exec/obidos/ASIN/0596517742/"&gt;Javascript: The Good
Parts&lt;/a&gt; de Douglas&amp;nbsp;Crockford.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sat, 30 Jan 2010 02:15:12 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-30:javascript/javascript-o-script-de-java.html</guid><category>Javascript</category></item><item><title>Slackware 13.1: Instalación y configuración básica</title><link>http://blog.elcodiguero.com/linux-unix/slackware-131-instalacion-y-configuracion-basica.html</link><description>&lt;div class="figure align-center"&gt;
&lt;img alt="Slackware" src="/imagenes/slackware.png" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Este artículo trata sobre Slackware 13.1, que no es la última
versión. Si bien la mayoría de lo que menciona sigue siendo válido,
existe un &lt;a class="reference external" href="/linux-unix/slackware-1337-instalacion-y-configuracion-basica.html"&gt;artículo más nuevo sobre Slackware&amp;nbsp;13.37&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.slackware.com"&gt;Slackware&lt;/a&gt; es la distribución de &lt;a class="reference external" href="http://es.wikipedia.org/wiki/GNU/Linux"&gt;&lt;span class="caps"&gt;GNU&lt;/span&gt;/Linux&lt;/a&gt; más antigua entre las
que siguen desarrollándose. Se caracteriza por su fama de complicada,
sin embargo una vez que el sistema está configurado el mantenimiento
necesario es mínimo, y resulta tan sencilla de manejar como cualquier
otra distribución. Lo único que el sistema pide a cambio de su
estabilidad y facilidad de mantenimiento es algo de tiempo leyendo
manuales y ayudas para comprender lo que sucede y por qué. La
instalación, configuración y uso de este sistema es probablemente la
mejor forma de aprender a manejar un sistema&amp;nbsp;Unix.&lt;/p&gt;
&lt;p&gt;Queda claro que le tengo un aprecio bastante grande, sin embargo soy
capaz de reconocer que, como todo, esta distribución no es perfecta y
tiene sus problemas. Y para solucionar los problemas suele requerirse un
conocimiento de lo que se hace. Es por eso que quiero compartir mi
experiencia instalando Slackware 13.0 en mi computadora &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Dell_Vostro"&gt;Dell Vostro
1000&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="instalacion"&gt;
&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;La instalación en sí no es un problema, recomiendo &lt;a class="reference external" href="http://manual.zenwalk.org/manual_es.pdf"&gt;esta guía de
instalación&lt;/a&gt; para aquellos que no hayan realizado este proceso antes.
La guía es para la distribución &lt;a class="reference external" href="http://www.zenwalk.org"&gt;ZenWalk&lt;/a&gt;, pero el proceso es casi el
mismo ya que &lt;em&gt;ZenWalk&lt;/em&gt; es una de las tantas distribuciones derivadas de&amp;nbsp;Slackware.&lt;/p&gt;
&lt;p&gt;Un punto a recordar es que, una vez en el programa de instalación, debe
iniciarse el proceso eligiendo la opción &lt;strong&gt;&lt;span class="caps"&gt;ADDSWAP&lt;/span&gt;&lt;/strong&gt; en el menú, o
&lt;strong&gt;&lt;span class="caps"&gt;TARGET&lt;/span&gt;&lt;/strong&gt;. Estos pasos deben hacerse primero ya que son los que
preparan el disco en el que se instalará, si no se hacen primero siempre
se llega a un punto en la instalación en el que se debe iniciar todo el
proceso de&amp;nbsp;nuevo.&lt;/p&gt;
&lt;p&gt;Y esto siempre requiere salir del programa de instalación y abrirlo de
nuevo, ya que la autodetección del disco &lt;span class="caps"&gt;CD&lt;/span&gt;/&lt;span class="caps"&gt;DVD&lt;/span&gt; de instalación funciona
solamente la primera vez que se&amp;nbsp;hace.&lt;/p&gt;
&lt;p&gt;De todas formas, la instalación con las opciones por defecto (&lt;em&gt;full
install&lt;/em&gt;, instalar todos los paquetes disponibles) puede iniciarse en
menos de 5 minutos y terminarse en más o menos media&amp;nbsp;hora.&lt;/p&gt;
&lt;p&gt;Al terminar la copia de archivos se hacen algunos pasos para la
configuración inicial del sistema, principalmente lo que respecta a la
ubicación geográfica, la configuración de red y la instalación del
gestor de arranque (&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Lilo_(Linux)"&gt;&lt;span class="caps"&gt;LILO&lt;/span&gt;&lt;/a&gt;). Una vez finalizados estos pasos, el
programa de instalación se cierra y se nos indica que debemos reiniciar
el&amp;nbsp;sistema.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuracion-inicial"&gt;
&lt;h2&gt;Configuración&amp;nbsp;inicial&lt;/h2&gt;
&lt;p&gt;Al arrancar el sistema, a diferencia de lo que sucede con otras
distribuciones, el entorno gráfico no se inicia por defecto. Se nos
presenta una pantalla de inicio de sesión en modo texto, en la que
deberemos entrar como el usuario root y con la contraseña
correspondiente (que habremos indicado en uno de los últimos pasos de la&amp;nbsp;instalación).&lt;/p&gt;
&lt;p&gt;Una vez dentro, hay varias tareas a&amp;nbsp;realizar.&lt;/p&gt;
&lt;div class="section" id="arrancar-por-defecto-el-entorno-grafico"&gt;
&lt;h3&gt;Arrancar por defecto el entorno&amp;nbsp;gráfico&lt;/h3&gt;
&lt;p&gt;Esto se hace editando el archivo &lt;em&gt;/etc/inittab&lt;/em&gt; y cambiando el &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Nivel_de_ejecución"&gt;nivel de
ejecución&lt;/a&gt; predeterminado. Lo haremos con &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Vim"&gt;vim&lt;/a&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
root&amp;#64;alvaro:/# vim /etc/inittab
&lt;/pre&gt;
&lt;p&gt;Una vez que se abra el archivo, hay que ir a la línea que&amp;nbsp;contiene&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Default runlevel. (Do not set to 0 or 6)
id:3:initdefault:
&lt;/pre&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;vim&lt;/tt&gt; no se parece en nada a los editores normales, por lo que para quien
lo usa por primera vez puede resultar bastante extraño. La forma más
sencilla de editar lo necesario y salir es llevar el cursor hacia el
&lt;strong&gt;3&lt;/strong&gt; en la línea mencionada antes, y presionar la siguiente secuencia
de&amp;nbsp;teclas:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;r&lt;/strong&gt; : Pondrá el editor en el modo adecuado para reemplazar un&amp;nbsp;caracter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4&lt;/strong&gt; : escribirá un 4, reemplazando al&amp;nbsp;3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;:wq&lt;/strong&gt; : este comando guarda el archivo y sale de &lt;tt class="docutils literal"&gt;vim&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Con esto, se indica que el nivel de ejecución por defecto debe ser el
nivel 4. Normalmente cualquier documentación de &lt;span class="caps"&gt;GNU&lt;/span&gt;/Linux que se
encuentre indica que el nivel 4 no se utiliza, y además casi todas las
demás distribuciones utilizan el nivel 5 para el modo gráfico. En esto
Slackware es diferente, en tanto que utiliza el nivel 4 para el modo
gráfico y no utiliza el nivel&amp;nbsp;5.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="crear-un-usuario-sin-privilegios"&gt;
&lt;h3&gt;Crear un usuario sin&amp;nbsp;privilegios&lt;/h3&gt;
&lt;p&gt;La forma usual de utilizar el sistema es como usuario sin privilegios.
Esto es, sin permiso para instalar programas o modificar el sistema,
únicamente se tiene permiso para hacer modificaciones en la carpeta
asignada al usuario. Este modo es ideal por la seguridad y para evitar
errores fatales, que casi todos hemos cometido alguna vez. Añadiremos un
usuario, entonces, con el comando adduser y escribiendo la información
que el comando&amp;nbsp;solicita.&lt;/p&gt;
&lt;p&gt;Para que este usuario sea usable, es necesario añadirlo a varios grupos:
&lt;em&gt;audio&lt;/em&gt; (para que pueda utilizar la tarjeta de sonido), &lt;em&gt;cdrom&lt;/em&gt;, &lt;em&gt;disk&lt;/em&gt;,
&lt;em&gt;plugdev&lt;/em&gt; (para que pueda utilizar dispositivos extraíbles), y &lt;em&gt;video&lt;/em&gt;. Esto
se puede hacer facilmente; en uno de los pasos &lt;tt class="docutils literal"&gt;adduser&lt;/tt&gt; muestra la
siguiente&amp;nbsp;línea:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Additional groups (comma separated) []:
&lt;/pre&gt;
&lt;p&gt;Si se presiona la flecha hacia arriba, se obtiene una lista de los
grupos&amp;nbsp;necesarios.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="utilizar-el-kernel-generic-en-lugar-de-huge"&gt;
&lt;h3&gt;Utilizar el kernel generic en lugar de&amp;nbsp;huge&lt;/h3&gt;
&lt;p&gt;Para la instalación, y por defecto en la instalación completa, Slackware
instala un núcleo Linux que tiene compilado casi todos los controladores
disponibles. Esto implica que el núcleo por defecto es bastante grande e
ineficiente, aunque en una instalación funciona bien. Para el uso diario
es mejor cambiar a un núcleo que incluya solamente lo mínimo, y en el
que los controladores estén incluidos en forma de módulos (de tal forma
que solamente se cargarán aquellos que sean necesarios).
Este cambio requiere de varias modificaciones, que quizás sea mejor
hacer desde el entorno gráfico (se requiere edición de archivos, y es
más fácil usar &lt;tt class="docutils literal"&gt;kwrite&lt;/tt&gt; que &lt;tt class="docutils literal"&gt;vim&lt;/tt&gt;). Por lo que reiniciar en este momento e
iniciar sesión como &lt;em&gt;root&lt;/em&gt; en el entorno gráfico puede ser la mejor&amp;nbsp;idea.&lt;/p&gt;
&lt;div class="section" id="vincular-al-kernel-correcto"&gt;
&lt;h4&gt;Vincular al kernel&amp;nbsp;correcto&lt;/h4&gt;
&lt;p&gt;Desde la consola, el siguiente&amp;nbsp;comando&lt;/p&gt;
&lt;pre class="literal-block"&gt;
cd /boot ; ls -l
&lt;/pre&gt;
&lt;p&gt;Debería mostrar algo como esto (eliminé algunas líneas&amp;nbsp;innecesarias):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
lrwxrwxrwx  1 root root      35 2008-12-18 00:49 System.map -&amp;gt; System.map-huge-smp-2.6.29.6-smp
-rw-r--r--  1 root root  965704 2008-11-21 02:20 System.map-generic-smp-2.6.29.6-smp
lrwxrwxrwx  1 root root      32 2008-12-18 00:49 vmlinuz -&amp;gt; vmlinuz-huge-smp-2.6.29.6-smp
-rw-r--r--  1 root root 2347888 2008-11-21 02:20 vmlinuz-generic-smp-2.6.29.6-smp
&lt;/pre&gt;
&lt;p&gt;Los archivos &lt;tt class="docutils literal"&gt;System.map&lt;/tt&gt; y &lt;tt class="docutils literal"&gt;vmlinuz&lt;/tt&gt; son enlaces que apuntan a las
versiones &lt;em&gt;huge&lt;/em&gt; del núcleo (esto puede verse indicado por las flechas &lt;strong&gt;-&amp;gt;&lt;/strong&gt;
junto al nombre del archivo). Debemos vincularlas a las versiones
&lt;em&gt;generic&lt;/em&gt;. Para esto, se utilizan los siguientes&amp;nbsp;comandos:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
rm System.map vmlinuz
ln -s vmlinuz-generic-smp-2.6.27.7-smp vmlinuz
ln -s System.map-generic-smp-2.6.27.7-smp System.map
&lt;/pre&gt;
&lt;p&gt;Obviamente conviene hacer uso del autocompletado en &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Bash"&gt;bash&lt;/a&gt;, para no
tener que recordar todo el nombre del archivo (presionar tecla&amp;nbsp;tabulador).&lt;/p&gt;
&lt;p&gt;El siguiente paso es crear el &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Initrd"&gt;archivo initrd&lt;/a&gt; correspondiente. Dado
que el núcleo genérico no tiene los controladores en el núcleo mismo
sino como módulos, es necesario brindarle algunos controladores
iniciales para que pueda encontrar el disco duro, leerlo, y cargar el
resto de los módulos que necesita. Estos controladores iniciales se
guardan en el archivo initrd.
Las instrucciones necesarias para crear este archivo están en el archivo
&lt;em&gt;&lt;span class="caps"&gt;README&lt;/span&gt;.initrd&lt;/em&gt; dentro de la carpeta &lt;em&gt;/boot&lt;/em&gt;. Básicamente, para un sistema
cuya partición principal es de tipo &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Ext3"&gt;ext3&lt;/a&gt;, y está en &lt;em&gt;/dev/sda1&lt;/em&gt; (la
primera partición primaria del primer disco &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Sata"&gt;&lt;span class="caps"&gt;SATA&lt;/span&gt;&lt;/a&gt;), el comando
necesario&amp;nbsp;es&lt;/p&gt;
&lt;pre class="literal-block"&gt;
mkinitrd -c -k 2.6.27.7-smp -m mbcache:jbd:ext3 -f ext3 -r /dev/sda1
&lt;/pre&gt;
&lt;p&gt;(dentro de la carpeta &lt;em&gt;/boot&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;El último paso ahora es configurar &lt;em&gt;&lt;span class="caps"&gt;LILO&lt;/span&gt;&lt;/em&gt; para que cargue este archivo
initrd. Habrá que editar el archivo &lt;em&gt;/etc/lilo.conf&lt;/em&gt;, buscar la sección
correspondiente y agregar una línea al&amp;nbsp;bloque&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Linux bootable partition config begins
image = /boot/vmlinuz
root = /dev/sda1
label = Linux
read-only
# Linux bootable partition config ends
&lt;/pre&gt;
&lt;p&gt;que deberá quedar&amp;nbsp;como&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Linux bootable partition config begins
initrd = /boot/initrd.gz
image = /boot/vmlinuz
root = /dev/sda1
label = Linux
read-only
# Linux bootable partition config ends
&lt;/pre&gt;
&lt;p&gt;Ya que estamos editando el archivo &lt;em&gt;lilo.conf&lt;/em&gt;, conviene también editar el
tiempo que se espera antes de arrancar el sistema, en la pantalla de
selección de &lt;a class="reference external" href="http://es.wikipedia.org/wiki/SO"&gt;&lt;span class="caps"&gt;SO&lt;/span&gt;&lt;/a&gt;. Por defecto este tiempo es 2 minutos, recomiendo
cambiarlo a un tiempo que sea corto pero permita interrumpir el proceso
en caso de ser necesario (por ejemplo, medio segundo).
Habrá que ir hacia el principio del archivo &lt;em&gt;lilo.conf&lt;/em&gt; y&amp;nbsp;cambiar:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# This is given in tenths of a second, so 600 for every minute:
timeout = 1200
&lt;/pre&gt;
&lt;p&gt;por&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# This is given in tenths of a second, so 600 for every minute:
timeout = 50
&lt;/pre&gt;
&lt;p&gt;Una vez realizados estos cambios, hay que re-instalar &lt;em&gt;&lt;span class="caps"&gt;LILO&lt;/span&gt;&lt;/em&gt;. Este proceso
es simplemente ejecutar el comando &lt;tt class="docutils literal"&gt;lilo&lt;/tt&gt; en la terminal. Recomiendo
ejecutar primero &lt;tt class="docutils literal"&gt;lilo &lt;span class="pre"&gt;-t&lt;/span&gt;&lt;/tt&gt; (hacer una simulación de la instalación) y
luego, si no hay errores (puede haber advertencias), ejecutar &lt;tt class="docutils literal"&gt;lilo&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Ahora, reiniciar el sistema nuevamente para cargar el núcleo&amp;nbsp;genérico.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configuracion-de-una-computadora-portatil"&gt;
&lt;h2&gt;Configuración de una computadora&amp;nbsp;portátil&lt;/h2&gt;
&lt;p&gt;Si tienes una computadora de escritorio, no se requieren más detalles de
configuración, todo debería haber sido reconocido y autoconfigurado. El
resto será cuestión de configurar los detalles del entorno gráfico. Pero
si como yo tienes una computadora portátil, habrá otros detalles a&amp;nbsp;configurar.&lt;/p&gt;
&lt;div class="section" id="red-inalambrica"&gt;
&lt;h3&gt;Red&amp;nbsp;inalámbrica&lt;/h3&gt;
&lt;p&gt;Es muy probable que el dispositivo de red inalámbrica no tenga
controladores para Linux. En este caso habrá que apelar a
&lt;a class="reference external" href="http://es.wikipedia.org/wiki/NDISwrapper"&gt;ndiswrapper&lt;/a&gt;, un programa que es capaz de utilizar los controladores
de Windows que estos dispositivos incluyen. ndiswrapper no está incluido
en la distribución de Slackware, pero puede conseguirse sin dificultad
en &lt;a class="reference external" href="http://www.slackbuilds.org"&gt;SlackBuilds.org&lt;/a&gt;.
También se pueden encontrar allí otros controladores nativos para Linux,
por ejemplo el controlador para las tarjetas&amp;nbsp;Broadcom.&lt;/p&gt;
&lt;p&gt;Vale la pena notar, sin embargo, que &lt;em&gt;ndiswrapper&lt;/em&gt; no puede funcionar
correctamente en un sistema de 64bit, no es capaz de cargar un
controlador de 32bit de Windows en un sistema de 64bit. Así que, aún si
tu equipo lo admite, asegúrate de tener los controladores necesarios
antes de instalar&amp;nbsp;Slackware64.&lt;/p&gt;
&lt;p&gt;Para el control de la red inalámbrica Slackware incluye el programa
&lt;a class="reference external" href="http://wicd.sourceforge.net"&gt;wicd&lt;/a&gt; en la carpeta &lt;em&gt;/extra&lt;/em&gt;. Bastará entonces con instalar el paquete
desde esa carpeta. Algo importante a recordar es que, para que &lt;tt class="docutils literal"&gt;wicd&lt;/tt&gt;
funcione correctamente, es necesario darle permisos de ejecución al
archivo &lt;em&gt;/etc/rc.d/rc.wicd&lt;/em&gt;, y &lt;strong&gt;remover la configuración que se encuentra
en el archivo /etc/rc.d/rc.inet1.conf&lt;/strong&gt;. Para lograr esto simplemente se
agrega una almohadilla (#) a toda línea de este archivo que no comience
con&amp;nbsp;una.&lt;/p&gt;
&lt;p&gt;Si todo va bien entonces, al reiniciar habrá un ícono de &lt;tt class="docutils literal"&gt;wicd&lt;/tt&gt; al lado
del reloj, pronto para conectarse a alguna de las redes&amp;nbsp;detectadas.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="synaptics-el-controlador-para-el-touchpad"&gt;
&lt;h3&gt;Synaptics, el controlador para el&amp;nbsp;touchpad&lt;/h3&gt;
&lt;p&gt;El controlador &lt;em&gt;synaptics&lt;/em&gt; viene incluido en Slackware, y el nuevo X.Org
incluido en Slackware 13 lo carga correctamente sin que haya que
configurar nada. Pero por defecto desactiva funciones como el hacer clic
tocando la superficie del &lt;em&gt;touchpad&lt;/em&gt;.
Para corregir este comportamiento, se pueden seguir dos caminos. El
primero es editar el archivo &lt;em&gt;xorg.conf&lt;/em&gt;, como se ha hecho toda la vida.
El segundo es modificar la configuración de &lt;a class="reference external" href="http://es.wikipedia.org/wiki/HAL_(software)"&gt;&lt;span class="caps"&gt;HAL&lt;/span&gt;&lt;/a&gt;, lo cual es más
acorde a la nueva forma de funcionar de&amp;nbsp;X.Org.&lt;/p&gt;
&lt;p&gt;El segundo camino es algo más complicado que el primero, pero requiere
conocer menos datos. Si se opta por modificar &lt;em&gt;xorg.conf&lt;/em&gt; deben conocerse
entre otras cosas el nombre de dispositivo y el protocolo a usar. Sin
embargo, modificando la configuración de &lt;span class="caps"&gt;HAL&lt;/span&gt; se indica al sistema qué
opciones dar por defecto a un dispositivo que use el controlador&amp;nbsp;synaptics.&lt;/p&gt;
&lt;p&gt;En mi caso habilité el botón primario (izquierdo) al tocar con el dedo
la superficie del &lt;em&gt;touchpad&lt;/em&gt;, el secundario (derecho) en la esquina
superior derecha, y el botón central en la esquina inferior derecha.
También habilité el desplazamiento (&lt;em&gt;scroll&lt;/em&gt;) horizontal y vertical
deslizando el dedo sobre los bordes inferior y derecho respectivamente.
&lt;a class="reference external" href="/archivos/99-x11-synaptics.fdi"&gt;El archivo completo está aquí&lt;/a&gt;. Las opciones posibles se detallan en
la página del manual de &lt;tt class="docutils literal"&gt;synaptics&lt;/tt&gt;.
Este archivo debe copiarse a la carpeta &lt;em&gt;/etc/hal/fdi/policy&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuracion-de-ahorro-de-energia"&gt;
&lt;h3&gt;Configuración de ahorro de&amp;nbsp;energía&lt;/h3&gt;
&lt;p&gt;En &lt;span class="caps"&gt;KDE4&lt;/span&gt;, el Gestor de Energía Guidance y el módulo de Gestión de Energía
en Preferencias del Sistema pueden configurar sin problemas casi todos
los aspectos de ahorro de energía, excepto por un detalle: qué se hace
cuando se presiona el botón de&amp;nbsp;encendido/apagado.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.linux.com/articles/54610"&gt;En esta página se explica cómo se debe configurar esto&lt;/a&gt;, el artículo
es viejo y la mayor parte de lo que se explica ya no es necesario
(gracias a la combinación Guidance/Preferencias del sistema mencionada
antes)
Los pasos para que al presionar el botón la computadora &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Hibernate_(OS_feature)"&gt;hiberne&lt;/a&gt; en
vez de apagarse son los&amp;nbsp;siguientes:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Cambiar a la carpeta &lt;em&gt;/etc/acpi&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Dejar en esta carpeta únicamente el archivo &lt;em&gt;acpi_handler.sh&lt;/em&gt; y crear
las carpetas &lt;em&gt;events&lt;/em&gt; and &lt;em&gt;actions&lt;/em&gt; en caso de ser&amp;nbsp;necesario.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Crear un archivo &lt;em&gt;/etc/acpi/actions/pwrbtn.sh&lt;/em&gt; con permisos de
ejecución, y con el siguiente&amp;nbsp;contenido:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#!/bin/sh
pm-hibernate
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Crear un archivo &lt;em&gt;/etc/acpi/events/pwrbtn&lt;/em&gt; con el siguiente&amp;nbsp;contenido:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
event=button[ /]power
action=/etc/acpi/actions/pwrbtn.sh
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Listo, al presionar el botón de encendido la computadora hibernará en
vez de apagarse. ¿Pero cómo restaurar?
Lo que hace la hibernación es mover el contenido de la memoria &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Memoria_de_acceso_aleatorio"&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/a&gt; y
de algunas otras cosillas a la partición &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Espacio_de_intercambio"&gt;swap&lt;/a&gt;. Para poder restaurar
el sistema es necesario indicarle al núcleo la partición de swap.
Hay que editar el archivo &lt;em&gt;lilo.conf&lt;/em&gt; de nuevo, buscar la sección de Linux
y añadirle la siguiente&amp;nbsp;línea:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
append = &amp;quot;resume=/dev/sda2&amp;quot;
&lt;/pre&gt;
&lt;p&gt;(donde &lt;em&gt;/dev/sda2&lt;/em&gt; es la partición de swap).
Para finalizar, hay que volver a ejecutar el comando &lt;tt class="docutils literal"&gt;lilo&lt;/tt&gt; (como antes,
primero &lt;tt class="docutils literal"&gt;lilo &lt;span class="pre"&gt;-t&lt;/span&gt;&lt;/tt&gt; para asegurarse de que todo es correcto, luego &lt;tt class="docutils literal"&gt;lilo&lt;/tt&gt; para
hacer la instalación). Con esto, si la máquina fue hibernada, el núcleo
restaurará el estado. En caso de que la máquina haya sido apagada, se
hará un arranque normal. En mi caso tengo configuradas dos secciones
idénticas, una con el resume y otra sin él, simplemente para hacer
pruebas de vez en&amp;nbsp;cuando.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="pasos-siguientes"&gt;
&lt;h2&gt;Pasos&amp;nbsp;siguientes&lt;/h2&gt;
&lt;div class="section" id="instalacion-de-programas"&gt;
&lt;h3&gt;Instalación de&amp;nbsp;programas&lt;/h3&gt;
&lt;p&gt;Slackware no tiene repositorios de paquetes, todos los paquetes
oficiales están en el &lt;span class="caps"&gt;DVD&lt;/span&gt; de instalación. Para instalar programas
externos se recomienda utilizar &lt;em&gt;SlackBuilds&lt;/em&gt;, que son scripts que
automatizan la generación de paquetes a partir de archivos de código
fuente. El repositorio más grande de SlackBuilds es &lt;a class="reference external" href="http://www.slackbuilds.org"&gt;slackbuilds.org&lt;/a&gt;,
y la forma más sencilla de utilizar ese repositorio es a través de una
herramienta llamada &lt;a class="reference external" href="http://sbopkg.org/"&gt;sbopkg&lt;/a&gt;.
La mayor parte de los programas tienen su SlackBuild, pero no todos.
Para estos últimos lo mejor es descargar el código fuente y crear un
paquete con &lt;a class="reference external" href="http://www.src2pkg.net/"&gt;src2pkg&lt;/a&gt;. En la mayoría de los casos la compilación será
exitosa. Si no lo es, no queda otra opción más que pedir ayuda en algún
foro para intentar resolver el&amp;nbsp;problema.&lt;/p&gt;
&lt;p&gt;Existen también repositorios extraoficiales de paquetes binarios, siendo
los más grandes &lt;a class="reference external" href="http://linuxpackages.net"&gt;LinuxPackages&lt;/a&gt; y &lt;a class="reference external" href="http://slacky.eu"&gt;Slacky&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gnome"&gt;
&lt;h3&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Por defecto Slackware no incluye el escritorio &lt;a class="reference external" href="http://gnome.org"&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&lt;/a&gt;. Hay varias
formas de incluirlo, la mejor hoy en día es &lt;a class="reference external" href="http://gnomeslackbuild.org/"&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt; SlackBuild&lt;/a&gt;. Incluye
no solamente el escritorio sino también programas relacionados con él,
como Rythmbox, Brasero, Mono y muchos más. Vale la pena tener
disponibles los repositorios para incluir también programas que no son
de &lt;span class="caps"&gt;GNOME&lt;/span&gt; pero Slackware no incluye y &lt;span class="caps"&gt;GSB&lt;/span&gt; sí, como&amp;nbsp;OpenOffice.&lt;/p&gt;
&lt;p&gt;Algunos enlaces&amp;nbsp;interesantes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.slackware.cl"&gt;SlackwareCL&lt;/a&gt;: comunidad de usuarios de Slackware de Chile, tienen
&lt;a class="reference external" href="http://www.slackware.cl/?q=guia_slackware"&gt;una guía&lt;/a&gt; que puede resultar&amp;nbsp;interesante.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://slacky.eu"&gt;Slacky&lt;/a&gt;: comunidad de usuarios de&amp;nbsp;Italia.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Se supone que esta es solamente una guía básica para tener el sistema
más o menos funcionando. Si algo no termina de quedar bien, recomiendo
el excelente foro de Slackware en &lt;a class="reference external" href="http://www.linuxquestions.org"&gt;LinuxQuestions&lt;/a&gt;, o el no tan grande
pero igualmente útil foro de Sistemas Unix en &lt;a class="reference external" href="http://www.forosdelweb.com"&gt;Foros del&amp;nbsp;Web&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sat, 30 Jan 2010 01:59:20 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-30:linux-unix/slackware-131-instalacion-y-configuracion-basica.html</guid><category>Linux &amp; UNIX</category></item><item><title>Consejos para evitar el SPAM</title><link>http://blog.elcodiguero.com/varios/consejos-para-evitar-el-spam.html</link><description>&lt;div class="section" id="introduccion"&gt;
&lt;h2&gt;Introducción&lt;/h2&gt;
&lt;p&gt;El spam, o correo basura es sin duda alguna la parte más molesta de
trabajar con correo electrónico.
Otros términos que se usan frecuentemente son &lt;span class="caps"&gt;UCE&lt;/span&gt; (siglas en inglés de
Correo electrónico comercial no solicitado) o &lt;span class="caps"&gt;UBE&lt;/span&gt; (siglas en inglés de
Correo electrónico masivo no solicitado).
La peor parte del correo basura es que los costos para quien lo envía es
casi nulo. Quien debe asumir el costo de transporte, almacenamiento y
filtrado (automático y manual) de los mensajes es el proveedor de correo
del destinatario. También hay que considerar la pérdida de tiempo que
insume el filtrado manual de los mensajes, sobretodo cuando la cuenta de
correo ha sido comprometida y recibe grandes cantidades de&amp;nbsp;basura.&lt;/p&gt;
&lt;p&gt;Si bien los proveedores de correo tienen cada vez mejores filtros
automáticos que nos evitan tener que eliminar manualmente la mayoría de
los mensajes basura, aún hay muchas precauciones y buenas prácticas que
uno puede tomar para evitar que la casilla se inunde.
Sobretodo es importante conocer y difundir las mejores prácticas, y
enseñar a los novatos a reconocer las amenazas que el spam trae consigo
(fraudes, estafas, venta de productos ilegales, etc). Aunque parezca
increíble, hay mucha gente sin experiencia que cae con facilidad en
estos&amp;nbsp;engaños.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="como-obtienen-las-direcciones-de-correo-los-spammers"&gt;
&lt;h2&gt;¿Cómo obtienen las direcciones de correo los&amp;nbsp;spammers&lt;/h2&gt;
&lt;div class="section" id="direcciones-de-correo-visibles-en-la-web"&gt;
&lt;h3&gt;Direcciones de correo visibles en la&amp;nbsp;web&lt;/h3&gt;
&lt;p&gt;Los spammers (aquellos quienes envían el correo basura) utilizan varios
métodos para recolectar nuestras direcciones de correo. Uno de ellos es
el uso de robots que recorren las páginas web buscando direcciones. Es
decir, si mi dirección de correo electrónico está públicamente visible
en una página web, tarde o temprano un robot spammer la&amp;nbsp;encontrará.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cadenas"&gt;
&lt;h3&gt;Cadenas&lt;/h3&gt;
&lt;p&gt;Por regla general las cadenas deberían eliminarse sin prestarles mucha
atención. Sin embargo, las personas suelen reenviarlas a todos sus
contactos, lo cual provoca que luego de 4 o 5 reenvíos el cuerpo del
mensaje contenga decenas de direcciones. Si de casualidad el mensaje
sigue reenviándose, podría caer eventualmente en poder de un spammer,
muchas veces debido a &lt;a class="reference external" href="http://es.wikipedia.org/wiki/Spam#Troyanos_y_ordenadores_zombis"&gt;troyanos u otra clase de programas maliciosos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sin entrar en los medios técnicos (que deberían estar a cargo de los
administradores de los servidores de correo, no de los usuarios), aún
hay algunas prácticas que pueden seguirse para evitar el daño causado
por el correo&amp;nbsp;basura.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="evitar-el-spam-antes-de-que-llegue"&gt;
&lt;h2&gt;Evitar el spam antes de que&amp;nbsp;llegue&lt;/h2&gt;
&lt;p&gt;La mejor manera de evitar el correo basura es &lt;strong&gt;evitar que nuestra
dirección de correo electrónico termine en la base de datos&lt;/strong&gt; de algún
delincuente (en muchos países del mundo el envío de spam es un&amp;nbsp;delito).&lt;/p&gt;
&lt;p&gt;Una buena práctica es &lt;strong&gt;tener al menos dos cuentas diferentes&lt;/strong&gt;, una
para uso personal y otra para suscripciones a sitios web. Con esto se
expone solamente una de las cuentas, que además como no se utiliza para
temas personales se puede reemplazar de forma sencilla si comienza a
recibir gran cantidad de mensajes&amp;nbsp;basura.&lt;/p&gt;
&lt;p&gt;En conjunción con esta práctica, es importante dejar la dirección de
correo solamente en sitios medianamente confiables, ya que uno nunca
sabe qué puede hacer el creador del sitio con la información&amp;nbsp;almacenada.&lt;/p&gt;
&lt;p&gt;Otro punto a considerar es el de las cadenas, como se mencionó arriba.
Todos tenemos algún conocido o amigo que no conoce (o pasa por alto) el
problema que conlleva el reenviar correo incluyendo todas las
direcciones en el campo Para, y sin borrar las cabeceras del mensaje que
quiere reenviar.
Entonces, para evitar que nuestra cuenta termine en alguna máquina
infectada por un troyano (que a su vez envíe la información al servidor
de un spammer), es importante explicarle a la gente lo importante que es
&lt;span class="caps"&gt;NO&lt;/span&gt; reenviar cadenas, o si se reenvían hacerlo con los cuidados
necesarios: enviando solamente el contenido y no las cabeceras de los
mensajes anteriores, y poniendo las direcciones en el campo &lt;span class="caps"&gt;CCO&lt;/span&gt; (copia
oculta) en vez de en &lt;span class="caps"&gt;CC&lt;/span&gt; para que así cada destinatario no vea las
direcciones de los&amp;nbsp;demás.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="que-hacer-cuando-ya-se-esta-recibiendo-correo-basura"&gt;
&lt;h2&gt;Qué hacer cuando ya se está recibiendo correo&amp;nbsp;basura&lt;/h2&gt;
&lt;p&gt;La mayoría de las veces la precaución no es suficiente: la cuenta
comienza a recibir correo basura de todas formas.
La estrategia en este punto deja de ser unicamente la protección de la
cuenta (aunque claramente esto se debe seguir haciendo) para pasar a ser
el pretender que la cuenta no existe. Esto es, cualquier respuesta que
se envíe a los spammers solamente logrará que nos llegue aún más basura.
Por este motivo nunca, bajo ningún concepto, se debe responder a ningún
correo no&amp;nbsp;solicitado.&lt;/p&gt;
&lt;p&gt;Pero no responder no es suficiente para fingir que la cuenta no existe:
el cliente de correo que usemos nos puede traicionar (Thunderbird,
Outlook Express, o incluso los clientes web de Gmail o Yahoo). Por esto
se recomienda &lt;span class="caps"&gt;NO&lt;/span&gt; abrir mensajes sospechosos, o abrirlos únicamente si el
cliente de correo está configurado para mostrarlos en modo texto.
El motivo es sencillo: si el cliente de correo admite formato de texto,
admitirá imágenes y otras clases de archivos externos. Es muy sencillo
incluir un código extra para que a la vez que se envía la petición para
obtener esa imagen o archivo externo, se envíe una confirmación de que
la cuenta de correo es&amp;nbsp;válida.&lt;/p&gt;
&lt;p&gt;No deberíamos tampoco hacer clic en ningún enlace que el correo incluya.
En el pasado ha habido fallos en todos los clientes de correo y
navegadores, que permiten ir hacia un sitio falseando su dirección. Es
decir, cuando uno piensa que está en el sitio de su banco, al que llegó
a través de un enlace en un correo, bien podría estar en realidad en un
sitio&amp;nbsp;fraudulento.&lt;/p&gt;
&lt;p&gt;Una de las reglas generales que siempre deberían seguirse es: &lt;strong&gt;si el
mensaje no fue solicitado, proceder con precaución&lt;/strong&gt;. No importa si
parece venir de una empresa o sitio web conocido, las direcciones de
correo electrónico son fácilmente falseables. Incluso algo tan confiable
a primera vista como &lt;a class="reference external" href="mailto:soporte&amp;#64;microsoft.com"&gt;soporte&amp;#64;microsoft.com&lt;/a&gt; podría ser en realidad un&amp;nbsp;fraude.&lt;/p&gt;
&lt;p&gt;Afortunadamente para nosotros los hispanohablantes, la mayoría del
correo basura se envía en inglés. Por lo que si no tenemos ningún
conocido que hable en inglés, se puede descartar con seguridad cualquier
correo que no esté en&amp;nbsp;español.&lt;/p&gt;
&lt;div class="section" id="nunca-comprar"&gt;
&lt;h3&gt;Nunca&amp;nbsp;comprar&lt;/h3&gt;
&lt;p&gt;Hay que reconocerlo, a veces las ofertas de lo que se vende son
interesantes. Pero con el bajo costo que tiene enviar un correo, una
sola compra seguramente compense el envío de millones de mensajes no
solicitados. Por este motivo es fundamental no comprar nada que se
oferte a través de spam, ya que las ventas que se consiguen son el
principal motivo para enviarlo (y eso sin contar que en la mayoría de
los casos no se trata de comerciantes sino de estafadores, por lo que
solamente terminaríamos perdiendo el&amp;nbsp;dinero).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="sustitucion-de-cuentas"&gt;
&lt;h2&gt;Sustitución de&amp;nbsp;cuentas&lt;/h2&gt;
&lt;p&gt;A pesar de todo lo comentado, hay que saber reconocer cuando nos han
vencido: si la cuenta de correo recibe demasiada basura, no queda otra
opción que reemplazarla. Por esto es importante tener una cuenta para
asuntos personales y otra para lo demás, es de suponer que la cuenta
utilizada solamente para asuntos personales estará más protegida, menos
gente la&amp;nbsp;conocerá.&lt;/p&gt;
&lt;p&gt;Lamentablemente, debido a los delincuentes una herramienta tan útil como
el correo electrónico a veces se vuelve inusable. Es imprescindible
entonces poner todos un granito de arena para evitar que la plaga se
vuelva aún&amp;nbsp;peor.&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Spam"&gt;Wikipedia:&amp;nbsp;Spam&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Mon, 18 Jan 2010 22:53:26 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-18:varios/consejos-para-evitar-el-spam.html</guid><category>Varios</category></item><item><title>¿Cómo escribir un archivo robots.txt?</title><link>http://blog.elcodiguero.com/varios/como-escribir-un-archivo-robotstxt.html</link><description>&lt;p&gt;Un archivo &lt;tt class="docutils literal"&gt;robots.txt&lt;/tt&gt; es un archivo de texto simple, que sirve para
indicar a los robots (en especial a los robots de los buscadores) qué
partes del sitio no debería indexar. Se considera que se puede indexar
cualquier carpeta o archivo que no esté explícitamente prohibido por
este&amp;nbsp;archivo.&lt;/p&gt;
&lt;p&gt;Los buscadores más importantes lo respetan (de otra forma su utilidad
sería bastante&amp;nbsp;escasa).&lt;/p&gt;
&lt;p&gt;Hay varios motivos para usar un archivo &lt;tt class="docutils literal"&gt;robots.txt&lt;/tt&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Evitar el indexado de las&amp;nbsp;imágenes&lt;/li&gt;
&lt;li&gt;Evitar que un robot consuma demasiado ancho de&amp;nbsp;banda&lt;/li&gt;
&lt;li&gt;Dirigir a los buscadores a las páginas especialmente preparadas para&amp;nbsp;ellos&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="como-crearlo"&gt;
&lt;h2&gt;¿Cómo&amp;nbsp;crearlo?&lt;/h2&gt;
&lt;p&gt;El archivo &lt;tt class="docutils literal"&gt;robots.txt&lt;/tt&gt; es un archivo de texto simple, que se ubica en
la raíz del sitio. El formato del archivo es extremadamente simple,
consta de 1 o más conjuntos, separados por una línea en blanco,&amp;nbsp;de:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Una línea &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;User-agent&lt;/span&gt;&lt;/tt&gt;, indicando el nombre de un robot o &amp;#8220;*&amp;#8221;;
(todos los&amp;nbsp;robots).&lt;/p&gt;
&lt;p&gt;Si bien la especificación de robotstxt.org no impone que se respeten
mayúsculas y minúsculas, para máxima compatibilidad es mejor
respetarlas. Esto es: escribir &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;User-agent&lt;/span&gt;&lt;/tt&gt; con la U mayúscula, y
escribir el nombre del robot tal cual se&amp;nbsp;identifica.&lt;/p&gt;
&lt;p&gt;Otro detalle a considerar es que no se aceptan expresiones regulares,
solamente el nombre de un robot (y solo uno) o el asterisco &amp;#8220;*&amp;#8221; para
que coincida con&amp;nbsp;todos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Una o más líneas &lt;tt class="docutils literal"&gt;Disallow&lt;/tt&gt; (no permitir), cada una indicando una
carpeta o archivo del sitio que no se quiere que el robot (indicado
por la línea User-agent) indexe. En esta línea tampoco se admiten
&lt;em&gt;comodines&lt;/em&gt;, debe darse la ruta completa. Cada línea &lt;tt class="docutils literal"&gt;Disallow&lt;/tt&gt;
debe contener solamente &lt;strong&gt;una&lt;/strong&gt;&amp;nbsp;ruta.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;He aquí un ejemplo, en el que se niega a todos los robots el acceso a la
carpeta &lt;em&gt;/admin&lt;/em&gt; y al &lt;em&gt;Googlebot&lt;/em&gt; el acceso a la carpeta &lt;em&gt;/imagenes&lt;/em&gt; y
al archivo &lt;em&gt;/javascript/ultrasecreto.js&lt;/em&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
User-agent: *
Disallow: /admin/

User-agent: Googlebot
Disallow: /imagenes/
Disallow: /javascript/ultrasecreto.js
&lt;/pre&gt;
&lt;p&gt;Los robots que respetan sus directivas lo buscan cada vez que se
conectan a un servidor, así que si el archivo no existe se generarán
muchos errores 404 causados por robots&amp;nbsp;buscándolo.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="como-saber-el-nombre-que-hay-que-usar-para-un-robot"&gt;
&lt;h2&gt;¿Cómo saber el nombre que hay que usar para un&amp;nbsp;robot?&lt;/h2&gt;
&lt;p&gt;La mayoría de los robots &amp;#8220;serios&amp;#8221; acompañan su cabecera User Agent con
una &lt;span class="caps"&gt;URL&lt;/span&gt; en la que se puede encontrar información sobre ellos, que suele
incluir los datos necesarios para guiar al robot usando el
&lt;tt class="docutils literal"&gt;robots.txt&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="seguridad"&gt;
&lt;h2&gt;Seguridad&lt;/h2&gt;
&lt;p&gt;Es importante destacar que aunque sirva para indicar a los robots por
qué partes del sitio no deben entrar, el archivo &lt;tt class="docutils literal"&gt;robots.txt&lt;/tt&gt; no debe
ser usado como mecanismo de seguridad. Los robots reciben la indicación
de no entrar, pero nada les impide hacerlo.
Además, todas las rutas que se indiquen quedan visibles para cualquiera
que quiera verlas, nada impide a cualquier usuario ver el archivo,
sobretodo considerando que siempre se encuentra en la misma ubicación
(la raíz del&amp;nbsp;sitio).&lt;/p&gt;
&lt;p&gt;Si se necesita seguridad, se necesita un sistema basado en programación
del lado del servidor, como siempre (ya sea un paquete &lt;span class="caps"&gt;PHP&lt;/span&gt; o
autenticación&amp;nbsp;&lt;span class="caps"&gt;HTTP&lt;/span&gt;)&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.robotstxt.org/robotstxt.html"&gt;Preguntas frecuentes sobre el archivo&amp;nbsp;robots.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Robots.txt"&gt;Wikipedia:&amp;nbsp;robots.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Mon, 18 Jan 2010 22:42:11 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-18:varios/como-escribir-un-archivo-robotstxt.html</guid><category>Varios</category></item><item><title>Validar una dirección de correo electrónico</title><link>http://blog.elcodiguero.com/php/validar-una-direccion-de-correo-electronico.html</link><description>&lt;p&gt;Cualquier sistema que utilice el correo electrónico debería tener algún
tipo de validación para evitar errores y datos basura. Pero ¿cómo
validar correctamente?
Antes que nada hay que tener por seguro que no hay forma (rápida y
práctica) de comprobar que una dirección de correo existe y tiene a un
usuario detrás, solamente se puede comprobar que la dirección tiene una
forma válida y apunta a un dominio&amp;nbsp;activo.&lt;/p&gt;
&lt;p&gt;Como suele suceder, el problema puede atacarse de diferentes formas. Hay
sistemas que simplemente validan que la información ingresada contenga
una arroba, hay otros que usan expresiones regulares complejas que
fallan en cuanto la dirección ingresada no es &lt;em&gt;común&lt;/em&gt; (aunque sea
válida).
La forma que voy a plantear no es novedosa, y es bastante sencilla, a la
vez que&amp;nbsp;efectiva.&lt;/p&gt;
&lt;div class="section" id="una-expresion-regular-permisiva"&gt;
&lt;h2&gt;Una expresión regular&amp;nbsp;permisiva&lt;/h2&gt;
&lt;p&gt;Debe poder validar a cualquier dirección de correo válida, esto quiere
decir que debe ser también bastante genérica. El patrón debe validar que
exista una arroba, un nombre de usuario, y una parte que indique el
dominio.
A pesar de que probablemente nunca se usen nombres de usuario complejos
en una dirección de correo, la verdad es que se puede usar cualquier
caracter en esta parte de la dirección.
Por lo que la expresión regular que debe validar esto no puede ser otra&amp;nbsp;que&lt;/p&gt;
&lt;pre class="literal-block"&gt;
.+
&lt;/pre&gt;
&lt;p&gt;Que representa a una cadena conteniendo al menos 1&amp;nbsp;caracter.&lt;/p&gt;
&lt;p&gt;El dominio es un poco más complicado. Muchos cometen el error de
solamente permitir dos o tres partes separadas por punto, o rechazar
direcciones cuya última parte contenga más de 3 caracteres. Con esto se
supone que incluyen a todos los dominios de primer nivel (&lt;em&gt;.com&lt;/em&gt;, &lt;em&gt;.net&lt;/em&gt;,
&lt;em&gt;.org&lt;/em&gt;&amp;#8230; y los de los países, que tienen 2 letras). Pero se olvidan que
Internet cambia, y ya tenemos &lt;a class="reference external" href="http://es.wikipedia.org/wiki/TLD"&gt;&lt;span class="caps"&gt;TLD&lt;/span&gt;&lt;/a&gt; &lt;em&gt;.info&lt;/em&gt; y &lt;em&gt;.aero&lt;/em&gt; (de cuatro letras) e
incluso &lt;em&gt;.museum&lt;/em&gt; (de&amp;nbsp;seis).&lt;/p&gt;
&lt;p&gt;Debido a esto, y considerando que lentamente están apareciendo los
dominios con &lt;strong&gt;ñ&lt;/strong&gt; y letras acentuadas, y que quizás pronto las
direcciones comiencen a aceptar más caracteres &lt;a class="reference external" href="http://es.wikipedia.org/wiki/UTF-8"&gt;&lt;span class="caps"&gt;UTF&lt;/span&gt;-8&lt;/a&gt;, es que también
para validar el dominio se debe utilizar un patrón genérico.
El único requisito en este paso es que contenga al menos 2 secuencias de
caracteres separadas por un punto. Debe haber al menos un punto, ya que
no hay dominios que no desciendan de alguno de los &lt;span class="caps"&gt;TLD&lt;/span&gt; (en realidad sí
hay direcciones válidas en entornos de red local que pertenecen a máquinas
sin nombre de dominio, pero una dirección así no sirve en&amp;nbsp;internet).&lt;/p&gt;
&lt;pre class="literal-block"&gt;
.+\..+
&lt;/pre&gt;
&lt;p&gt;Este patrón representa a &lt;em&gt;una secuencia de caracteres no vacía, seguida
de un punto, y seguida de otra secuencia de caracteres no vacía&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Finalmente, juntando las partes el patrón&amp;nbsp;queda:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
^.+&amp;#64;.+\..+$
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="validacion-del-dominio-con-php"&gt;
&lt;h2&gt;Validación del dominio con&amp;nbsp;&lt;span class="caps"&gt;PHP&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Una versión anterior de este artículo recomendaba validar el dominio con
&lt;a class="reference external" href="http://php.net/checkdnsrr"&gt;checkdnsrr()&lt;/a&gt;. Esta parte ha sido eliminada, y el motivo es bastante
simple, a decir&amp;nbsp;verdad.&lt;/p&gt;
&lt;p&gt;La función &lt;tt class="docutils literal"&gt;checkdnsrr&lt;/tt&gt; solamente puede determinar si un dominio está
registrado, y con el parámetro adecuado permite saber si el dominio
tiene un registro &lt;a class="reference external" href="http://en.wikipedia.org/wiki/List_of_DNS_record_types"&gt;&lt;span class="caps"&gt;MX&lt;/span&gt; (Mail eXchange)&lt;/a&gt; asociado (es decir, si es capaz
de recibir correo). Pero una entrada &lt;a class="reference external" href="http://es.wikipedia.org/wiki/DNS"&gt;&lt;span class="caps"&gt;DNS&lt;/span&gt;&lt;/a&gt; correcta no implica que
realmente haya un servidor funcionando en el dominio.
Otras opciones, como &lt;a class="reference external" href="http://php.net/gethostbyname"&gt;gethostbyname&lt;/a&gt; por ejemplo, tienen sus propios&amp;nbsp;problemas:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Pueden fallar si el servidor remoto está temporalmente fuera de
servicio, lo cual no es tan extraño en servidores&amp;nbsp;pequeños.&lt;/li&gt;
&lt;li&gt;Si el equipo remoto no existe, la llamada a &lt;tt class="docutils literal"&gt;gethostbyname&lt;/tt&gt; demora
mucho en completarse, no por &lt;span class="caps"&gt;PHP&lt;/span&gt; sino por la forma en la que funciona
el protocolo&amp;nbsp;&lt;span class="caps"&gt;DNS&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Podría haber un fallo de conectividad temporal de parte del servidor
que ejecuta el programa &lt;span class="caps"&gt;PHP&lt;/span&gt;, lo cual provocaría que la función&amp;nbsp;fallase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Por estos motivos es que la validación del dominio funcionará en la
mayoría de los casos pero fallará en otros, y si no se puede saber que
será exitosa en &lt;strong&gt;todos&lt;/strong&gt; los casos, no se puede confiar en&amp;nbsp;ella.&lt;/p&gt;
&lt;p&gt;De todas formas se incluyen los enlaces a las páginas del manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;
que corresponden a estas funciones, para que cada uno decida si hacer la
validación o&amp;nbsp;no.&lt;/p&gt;
&lt;p&gt;Vale recordar lo que se mencionó al principio: no existe forma práctica
de comprobar con certeza que la dirección es válida aún cuando se
pudiese validar el dominio, dado que la dirección podría ser válida en
forma pero no existir en el servidor, o podría estar mal escrita por
error.
La única manera de saber con seguridad si la dirección es válida es
enviar un correo de confirmación antes de habilitar al usuario a
continuar en el sitio. Pero esto tampoco es la solución en todos los
casos; a veces solicitar confirmación al usuario no tiene sentido (por
ejempo en un formulario de&amp;nbsp;contacto).&lt;/p&gt;
&lt;p&gt;A continuación el código de mi función de&amp;nbsp;validación:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validar_correo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$correo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/^.+&amp;#64;(.+\..+)$/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$correo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;A modo de resumen: la validación de direcciones de correo es compleja y
no vale la pena intentar validar más que la forma. La mayoría de los
métodos más estrictos con los que uno se puede llegar a encontrar suelen
rechazar direcciones perfectamente correctas (aunque relativamente&amp;nbsp;raras).&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/TLD"&gt;Wikipedia:&amp;nbsp;&lt;span class="caps"&gt;TLD&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/UTF-8"&gt;Wikipedia:&amp;nbsp;&lt;span class="caps"&gt;UTF&lt;/span&gt;-8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/DNS"&gt;Wikipedia:&amp;nbsp;&lt;span class="caps"&gt;DNS&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Expresiones_regulares"&gt;Wikipedia: Expresiones&amp;nbsp;regulares&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://php.net/checkdnsrr"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;checkdnsrr()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://php.net/gethostbyname"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;gethostbyname&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://php.net/preg_match"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;preg_match&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Mon, 11 Jan 2010 23:21:45 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-11:php/validar-una-direccion-de-correo-electronico.html</guid><category>PHP</category></item><item><title>Validar parámetros enteros con PHP</title><link>http://blog.elcodiguero.com/php/validar-parametros-enteros-con-php.html</link><description>&lt;div class="section" id="introduccion"&gt;
&lt;h2&gt;Introducción&lt;/h2&gt;
&lt;p&gt;¿Cómo hacer para estar seguro de que un parámetro obtenido por &lt;span class="caps"&gt;GET&lt;/span&gt; (o
&lt;span class="caps"&gt;POST&lt;/span&gt;) es un número entero?
Intentando resolver esta pregunta he intentado varias técnicas a lo
largo del tiempo, y haciendo esas pruebas he llegado a entender más
sobre cómo funcionan las comparaciones y las conversiones automáticas de
tipos en&amp;nbsp;&lt;span class="caps"&gt;PHP&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="convirtiendo-a-un-valor-entero"&gt;
&lt;h2&gt;Convirtiendo a un valor&amp;nbsp;entero&lt;/h2&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;intval&lt;/tt&gt; es una función que obtiene el valor entero de lo que se le
pase como parámetro. Es equivalente a forzar una conversión anteponiendo
&lt;tt class="docutils literal"&gt;(int)&lt;/tt&gt; a un valor. Es decir, las siguientes dos líneas son&amp;nbsp;equivalentes:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt; &lt;span class="nv"&gt;$valor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parametro&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt; &lt;span class="nv"&gt;$valor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$parametro&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;De esta forma se obtiene un valor entero en todos los casos, aunque no
necesariamente este valor es igual al que queremos validar, ya que
cualquier cadena puede ser convertida a un entero, incluso una cadena
que no contenga números (si no comienza con números, su valor es&amp;nbsp;cero).&lt;/p&gt;
&lt;p&gt;Pero una conversión no es una validación, así que debemos hallar una
forma de asegurarnos de que el valor almacenado en &lt;tt class="docutils literal"&gt;$parametro&lt;/tt&gt; es un
número&amp;nbsp;entero.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="primer-intento-is-int"&gt;
&lt;h2&gt;Primer intento:&amp;nbsp;is_int&lt;/h2&gt;
&lt;p&gt;La primera idea es comprobar con la función &lt;a class="reference external" href="http://www.php.net/is_int"&gt;is_int&lt;/a&gt;.
Esta función, como su nombre lo indica, comprueba que una variable sea
de tipo entero, devolviendo &lt;tt class="docutils literal"&gt;true&lt;/tt&gt; en caso de que lo sea, y &lt;tt class="docutils literal"&gt;false&lt;/tt&gt;
en caso&amp;nbsp;contrario.&lt;/p&gt;
&lt;p&gt;Parece ideal, sin embargo hay un problema: los valores que se reciben
vía &lt;span class="caps"&gt;GET&lt;/span&gt; o &lt;span class="caps"&gt;POST&lt;/span&gt; son siempre cadenas, sin importar su contenido. El &lt;a class="reference external" href="http://www.php.net/is_int"&gt;manual
de la función&lt;/a&gt; lo&amp;nbsp;advierte:&lt;/p&gt;
&lt;blockquote&gt;
Para probar si una variable es un número o una cadena numérica (como
en el caso de la entrada de un formulario, que es siempre una
cadena), debe usar &lt;tt class="docutils literal"&gt;is_numeric()&lt;/tt&gt;.&lt;/blockquote&gt;
&lt;p&gt;Lo podemos comprobar fácilmente: Supongamos que &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$_GET[&amp;#8216;var&amp;#8217;]&lt;/span&gt;&lt;/tt&gt; tiene
el valor &lt;em&gt;12&lt;/em&gt;, es decir, recibimos una &lt;span class="caps"&gt;URL&lt;/span&gt; conteniendo &lt;em&gt;&amp;amp;var=12&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;En estas&amp;nbsp;condiciones,&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt; &lt;span class="nb"&gt;gettype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// devuelve &amp;quot;string&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// devuelve false&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="segundo-intento-is-numeric"&gt;
&lt;h2&gt;Segundo intento:&amp;nbsp;is_numeric&lt;/h2&gt;
&lt;p&gt;La función &lt;a class="reference external" href="http://www.php.net/is_numeric"&gt;is_numeric&lt;/a&gt; toma cualquier una variable (o valor) de
cualquier tipo, y comprueba si tiene formato de número. Esto es, si es
una cadena que representa un entero, o un flotante en cualquiera de las
notaciones válidas para &lt;span class="caps"&gt;PHP&lt;/span&gt;.
Serviría para nuestro propósito si no fuese porque no acepta solamente
enteros sino también números con coma (punto, en realidad), y por lo
tanto devolverá &lt;tt class="docutils literal"&gt;true&lt;/tt&gt; para un valor como 10.33, por&amp;nbsp;ejemplo.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tercer-intento-comparacion-con-entero"&gt;
&lt;h2&gt;Tercer intento: comparación con&amp;nbsp;entero&lt;/h2&gt;
&lt;p&gt;Otra opción puede ser hacer una comparación&amp;nbsp;como&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;En teoría funcionaría, pero no es así.
&lt;span class="caps"&gt;PHP&lt;/span&gt; es un tanto especial en lo que refiere a las conversiones de tipos:
si uno de los valores a comparar es entero, convertirá al otro en entero
antes de compararlos. Por lo tanto, la comparación propuesta &lt;strong&gt;siempre&lt;/strong&gt;
será cierta, ya que el lado izquierdo de la comparación será convertido.
El resultado es que el código anterior es equivalente al siguiente, que
es siempre verdadero y por lo tanto no nos&amp;nbsp;sirve:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="cuarto-intento-comparacion-de-identicos"&gt;
&lt;h2&gt;Cuarto intento: comparación de&amp;nbsp;idénticos&lt;/h2&gt;
&lt;p&gt;Uno podría pensar que si &lt;span class="caps"&gt;PHP&lt;/span&gt; convierte a entero ambos lados y por eso no
sirve el ejemplo anterior, bastaría con usar el operador &lt;tt class="docutils literal"&gt;===&lt;/tt&gt;
(comparación sin conversión) para saltear el problema. Pero no es así.
Los valores siempre serán de tipos diferentes (ya que las variables &lt;span class="caps"&gt;GET&lt;/span&gt;
son siempre cadenas) y por lo tanto la comparación sería siempre&amp;nbsp;falsa.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="quinto-intento-complicado-pero-funciona"&gt;
&lt;h2&gt;Quinto intento: complicado pero&amp;nbsp;funciona&lt;/h2&gt;
&lt;p&gt;Finalmente decidí probar algo más complejo pero que evita todos los
problemas de los casos&amp;nbsp;anteriores.&lt;/p&gt;
&lt;p&gt;¿Qué hacer? La comparación con un entero es necesaria, porque después de
todo lo que necesitamos es un entero, pero no podemos comparar una
cadena con un entero porque la cadena será convertida.
La solución que se me ocurrió es simplemente volver a convertir al
entero en una&amp;nbsp;cadena:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;o&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'var'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Por ejemplo, si &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$_GET[&amp;#8216;var&amp;#8217;]&lt;/span&gt;&lt;/tt&gt; tiene el valor &amp;#8220;12&amp;#8221;, el proceso&amp;nbsp;sería:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;12&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; entero 12
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;12&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; cadena &amp;quot;12&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;12&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;12&amp;quot;&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; verdadero, contienen el mismo valor.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Si ahora &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$_GET[&amp;#8216;var&amp;#8217;]&lt;/span&gt;&lt;/tt&gt; tiene el valor&amp;nbsp;&amp;#8220;hola&amp;#8221;:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hola&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; entero 0
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hola&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; cadena &amp;quot;0&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hola&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; falso, la cadena pasada no es un entero.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Y finalmente, el caso que hace que las conversiones fallen: una cadena
con valores numéricos y letras. Supongamos que &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$_GET[&amp;#8216;var&amp;#8217;]&lt;/span&gt;&lt;/tt&gt; tiene el
valor&amp;nbsp;&amp;#8220;12hola&amp;#8221;.&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;12hola&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; entero 12
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;12hola&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; cadena &amp;quot;12&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;12hola&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;12&amp;quot;&lt;/span&gt; &lt;span class="c1"&gt;# --&amp;gt; falso. Al evitar la conversión automática, no hay problemas.&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="solucion-correcta-ctype-digit"&gt;
&lt;h2&gt;Solución correcta:&amp;nbsp;ctype_digit&lt;/h2&gt;
&lt;p&gt;Esta es una actualización de este artículo. Luego de mucho tiempo, di
casi de casualidad con esta función, que sirve justamente para validar
si una cadena contiene únicamente dígitos. Es increíble cómo uno puede
utilizar un lenguaje durante años, y seguir encontrando cosas que no
conocía, en particular esta función que no es para nada nueva, existe
desde &lt;span class="caps"&gt;PHP&lt;/span&gt;&amp;nbsp;4.0.4&lt;/p&gt;
&lt;p&gt;Es importante aclarar que esta función sirve solamente para variables de
tipo cadena, ya que lo que hace es comprobar que cada caracter en el
valor pasado como parámetro está en el rango &lt;span class="caps"&gt;ASCII&lt;/span&gt; que corresponde a los
dígitos del 0 al 9 (valores entre 48 y 57 inclusive). No sirve para
datos de tipo entero, y esto está claramente especificado en su &lt;a class="reference external" href="http://www.php.net/ctype_digit"&gt;página
del manual&lt;/a&gt;. También es preciso tener en cuenta que &lt;em&gt;un enterno negativo
no será admitido&lt;/em&gt;, debido a que &amp;#8220;-&amp;#8221; no es un&amp;nbsp;dígito.&lt;/p&gt;
&lt;p&gt;Todo el trabajo que pasé intentando validar un entero se reduce,
entonces&amp;nbsp;a:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ctype_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$variable&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;La variable es entera&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/manual/en/language.types.type-juggling.php"&gt;Manipulación de tipos en&amp;nbsp;&lt;span class="caps"&gt;PHP&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/is_int"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;is_int&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/is_numeric"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;is_numeric&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/gettype"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;gettype&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/ctype_digit"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;ctype_digit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Fri, 08 Jan 2010 00:01:49 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-08:php/validar-parametros-enteros-con-php.html</guid><category>PHP</category></item><item><title>CSV: simple y versátil</title><link>http://blog.elcodiguero.com/varios/csv-simple-y-versatil.html</link><description>&lt;div class="section" id="que-es-un-archivo-csv"&gt;
&lt;h2&gt;¿Qué es un archivo&amp;nbsp;&lt;span class="caps"&gt;CSV&lt;/span&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/CSV"&gt;&lt;span class="caps"&gt;CSV&lt;/span&gt;&lt;/a&gt; es un tipo de archivo de texto que contiene son archivos de
&lt;em&gt;valores separados por comas&lt;/em&gt; (de ahí su nombre). Es un formato bien
simple y práctico.
No tiene una documentación oficial, pero buscando en internet encontré
el &lt;a class="reference external" href="http://www.rfc-editor.org/rfc/rfc4180.txt"&gt;&lt;span class="caps"&gt;RFC&lt;/span&gt; 4180, Common Format and &lt;span class="caps"&gt;MIME&lt;/span&gt; Type for Comma-Separated Values
(&lt;span class="caps"&gt;CSV&lt;/span&gt;) Files&lt;/a&gt;. Al contrario de muchos &lt;span class="caps"&gt;RFC&lt;/span&gt;, que son especificaciones
completas de formatos y protocolos, este parece ser simplemente un
resumen de las características de los &lt;span class="caps"&gt;CSV&lt;/span&gt;, y un intento por documentar
formalmente el&amp;nbsp;formato.&lt;/p&gt;
&lt;p&gt;Hay 5 reglas básicas que definen un archivo &lt;span class="caps"&gt;CSV&lt;/span&gt;, a&amp;nbsp;saber:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Cada campo se separa con una coma, al último de la fila no le sigue
una&amp;nbsp;coma&lt;/li&gt;
&lt;li&gt;No se toman en cuenta posibles espacios entre las comas&amp;nbsp;separadoras.&lt;/li&gt;
&lt;li&gt;Cada fila se separa con un salto de línea (&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CRLF&lt;/span&gt;&lt;/tt&gt;, aunque se
soporta también &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;LF&lt;/span&gt;&lt;/tt&gt;)&lt;/li&gt;
&lt;li&gt;Los campos pueden o no estar delimitados por comillas (necesario en
particular cuando se necesita incluir una coma en el campo o un salto
de&amp;nbsp;línea).&lt;/li&gt;
&lt;li&gt;Si se necesita incluir comillas &amp;#8220;, hay que duplicarlas: para que se
tome &amp;#8220;b&amp;#8221; como el campo completo, hay que escribirlo como&amp;nbsp;&amp;#8221;&amp;#8220;b&amp;#8221;&amp;#8220;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="detalles"&gt;
&lt;h2&gt;Detalles&lt;/h2&gt;
&lt;p&gt;En caso de querer ponerlo para descargar, es bueno saber que el tipo
&lt;span class="caps"&gt;MIME&lt;/span&gt; del formato es &lt;tt class="docutils literal"&gt;text/csv&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;La codificación de caracteres por defecto se toma como &lt;strong&gt;&lt;span class="caps"&gt;US&lt;/span&gt;-&lt;span class="caps"&gt;ASCII&lt;/span&gt;&lt;/strong&gt;,
pero puede usarse el parámetro &lt;tt class="docutils literal"&gt;charset&lt;/tt&gt; de la cabecera
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Content-Type&lt;/span&gt;&lt;/tt&gt; para especificar otra. Si no se usa &lt;tt class="docutils literal"&gt;charset&lt;/tt&gt;, el
programa que trabaje con el archivo &lt;span class="caps"&gt;CSV&lt;/span&gt; deberá decidir qué codificación
usa.
Este es el caso más común, ya que cuando uno abre el archivo con un
programa como Excel u &lt;a class="reference external" href="http://www.openoffice.org/"&gt;OpenOffice&lt;/a&gt; Calc, no existe una cabecera que
indique el tipo&amp;nbsp;&lt;span class="caps"&gt;MIME&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt; provee 2 funciones para el manejo de estos archivos: &lt;a class="reference external" href="http://www.php.net/fputcsv"&gt;fputcsv&lt;/a&gt; y
&lt;a class="reference external" href="http://www.php.net/fgetcsv"&gt;fgetcsv&lt;/a&gt;.
&lt;tt class="docutils literal"&gt;fgetcsv&lt;/tt&gt; extrae los campos separados por comas de un archivo, línea
por línea, mientras que &lt;tt class="docutils literal"&gt;fputcsv&lt;/tt&gt; permite hacer el proceso&amp;nbsp;contrario.&lt;/p&gt;
&lt;p&gt;Python incluye el &lt;a class="reference external" href="http://docs.python.org/library/csv.html"&gt;módulo csv&lt;/a&gt;, que permite leer y escribir fácilmente
tuplas y listas hacia &lt;span class="caps"&gt;CSV&lt;/span&gt; y&amp;nbsp;viceversa.&lt;/p&gt;
&lt;p&gt;Siendo un formato tan simple, resulta muy práctico para transmitir o
almacenar información que luego debe ser procesada por otro sistema.
También es la forma más sencilla de generar un archivo que pueda ser
leído por los programas de hojas de cálculo (Excel, Calc, Gnumeric,
etc), aunque no guarda información de formato de texto (negritas,
colores, tamaños de letra,&amp;nbsp;etc).&lt;/p&gt;
&lt;p&gt;En caso de que no sea suficiente, siempre se puede recurrir a formatos
más complejos, como &lt;a class="reference external" href="http://es.wikipedia.org/wiki/XML"&gt;&lt;span class="caps"&gt;XML&lt;/span&gt;&lt;/a&gt; o &lt;a class="reference external" href="http://es.wikipedia.org/wiki/JSON"&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados:&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/CSV"&gt;Wikipedia:&amp;nbsp;&lt;span class="caps"&gt;CSV&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.rfc-editor.org/rfc/rfc4180.txt"&gt;&lt;span class="caps"&gt;RFC&lt;/span&gt; 4180, “Common Format and &lt;span class="caps"&gt;MIME&lt;/span&gt; Type for Comma-Separated Values
(&lt;span class="caps"&gt;CSV&lt;/span&gt;)&amp;nbsp;Files”&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 07 Jan 2010 22:09:38 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-07:varios/csv-simple-y-versatil.html</guid><category>Varios</category></item><item><title>Transacciones en MySQL</title><link>http://blog.elcodiguero.com/mysql/transacciones-en-mysql.html</link><description>&lt;p&gt;Hay situaciones en las que necesitamos hacer más de una consulta al
mismo tiempo, y todas tienen que ser correctas para que los datos sean
consistentes y tengan sentido, de otro modo tendríamos información
&amp;#8220;colgada&amp;#8221; y desvinculada.
En el mejor de los casos esta información provocaría un desperdicio de
espacio en la base de datos, pero lo más probable es que además lleve a
errores a la hora de mostrarse y de hacer cálculos con&amp;nbsp;ella.&lt;/p&gt;
&lt;p&gt;Parte de este problema se resuelve teniendo una buena estructura de base
de datos, con claves foráneas relacionando los campos&amp;nbsp;correspondientes.&lt;/p&gt;
&lt;p&gt;Pero para mayor seguridad, es bueno usar lo que en jerga &lt;em&gt;databasera&lt;/em&gt; se
conoce como&amp;nbsp;transacciones.&lt;/p&gt;
&lt;p&gt;Las transacciones sirven para asegurar la consistencia de la
información, asegurando que un conjunto de sentencias se ejecuten
correctamente, o no se&amp;nbsp;ejecuten.&lt;/p&gt;
&lt;p&gt;En un principio MySQL no las soportaba, esto cambió a partir de la
versión 4, y con el uso de tablas &lt;a class="reference external" href="http://es.wikipedia.org/wiki/InnoDB"&gt;InnoDB&lt;/a&gt;. Nótese que el motor por
defecto para las tablas en &lt;a class="reference external" href="http://www.mysql.com"&gt;MySQL&lt;/a&gt; hasta las versiones 5.* era
&lt;a class="reference external" href="http://es.wikipedia.org/wiki/MyISAM"&gt;MyISAM&lt;/a&gt;, que no soporta&amp;nbsp;transacciones.&lt;/p&gt;
&lt;div class="section" id="un-ejemplo"&gt;
&lt;h2&gt;Un&amp;nbsp;ejemplo&lt;/h2&gt;
&lt;p&gt;Supongamos que un sitio web bancario tiene 2 usuarios, ambos trabajando
sobre la misma&amp;nbsp;cuenta.&lt;/p&gt;
&lt;p&gt;El usuario 1 pide incrementar su saldo en 10, mientras que el usuario 2
pide disminuirlo (a través de un formulario, por&amp;nbsp;ejemplo)&lt;/p&gt;
&lt;p&gt;El programador del sistema no puede decidir el orden en el que se
ejecutarán las consultas, así que bien podría suceder lo&amp;nbsp;siguiente:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="n"&gt;bal1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;FROM&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuentas&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuenta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="c1"&gt;-- usuario 1
&lt;/span&gt;&lt;span class="n"&gt;bal2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;FROM&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuentas&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuenta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="c1"&gt;-- usuario 2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;En este punto, existen dos copias de la aplicación que contienen una
variable &lt;tt class="docutils literal"&gt;$balance&lt;/tt&gt; cada una. Supongamos que ambas necesitan actualizar el
valor en la base de&amp;nbsp;datos:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuentas&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;SET&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bal1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuenta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="c1"&gt;-- usuario 1
&lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuentas&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;SET&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bal2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;cuenta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="c1"&gt;-- usuario 2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;El resultado es que ambas copias del programa ejecutaron sus consultas
con la información de balance que tenían, por lo que el resultado final
es como si la consulta del usuario 1 no se hubiera ejecutado nunca, ya
que el usuario 2 actualiza el registro con información vieja. Al final,
en vez de quedar con el mismo saldo, la cuenta termina perdiendo&amp;nbsp;10.&lt;/p&gt;
&lt;p&gt;Lo que se necesita para este conjunto de consultas, es lo que se
denomina &lt;a class="reference external" href="http://es.wikipedia.org/wiki/ACID"&gt;&lt;span class="caps"&gt;ACID&lt;/span&gt;&lt;/a&gt;, un acrónimo inglés que quiere decir Atomicidad,
Consistencia, Aislamiento y Durabilidad. Recomiendo leer la información
de Wikipedia para entender de qué se trata esto, pero lo importante es
lo siguiente: las transacciones son un conjunto de consultas que se
ejecutan como si fuesen una. Y por esto, permiten asegurar la
consistencia de los datos, ya que si en mitad del proceso una consulta
falla, todos los cambios producidos por consultas anteriores pueden ser&amp;nbsp;revertidos.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="como-usar-transacciones"&gt;
&lt;h2&gt;¿Cómo usar&amp;nbsp;transacciones?&lt;/h2&gt;
&lt;p&gt;Usar transacciones es muy simple: antes de ejecutar la primer consulta,
se ejecuta una que solamente contiene &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;BEGIN&lt;/span&gt;&lt;/tt&gt;.
Luego se ejecutan las consultas que deban ejecutarse. Si éstas resultan
exitosas, se termina la transacción con &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COMMIT&lt;/span&gt;&lt;/tt&gt;, lo cual provoca
que los cambios hechos por las consultas anteriores sean permanentes.
Si las consultas fallan en algún paso, se puede volver al estado
anterior al comienzo de la transacción ejecutando &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;ROLLBACK&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Aunque los datos no sean realmente escritos a la o las tablas
involucradas hasta ejecutar el &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COMMIT&lt;/span&gt;&lt;/tt&gt;, las consultas devuelven lo
mismo que si lo fueran, es decir, para saber si una consulta falló basta
con ver el valor de retorno de &lt;tt class="docutils literal"&gt;mysql_query&lt;/tt&gt; y para ver el número de
filas afectadas sigue valiendo usar &lt;tt class="docutils literal"&gt;mysql_num_rows&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Mientras la transacción está ejecutándose, los datos (en el caso de
InnoDB las filas y en el caso de MyISAM las tablas) afectados quedan
bloqueados, nadie puede acceder a ellos. Cualquier consulta que tenga
que ver con los mismos datos será demorada hasta que la transacción
termine.
Esto implica que usar transacciones es un poco más lento que no usarlas,
pero a la vez implica que los datos involucrados no pueden ser
modificados por otra copia de la aplicación, y por lo tanto se evita la
situación planteada al principio como&amp;nbsp;ejemplo.&lt;/p&gt;
&lt;p&gt;Dicha situación, implementada de forma &amp;#8220;transaccional&amp;#8221; en &lt;span class="caps"&gt;PHP&lt;/span&gt;,&amp;nbsp;quedaría:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;span class="caps"&gt;BEGIN&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="nv"&gt;$balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.....&lt;/span&gt; &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;span class="caps"&gt;SELECT&lt;/span&gt; balance &lt;span class="caps"&gt;FROM&lt;/span&gt; cuentas &lt;span class="caps"&gt;WHERE&lt;/span&gt; cuenta=X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="nv"&gt;$resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;span class="caps"&gt;UPDATE&lt;/span&gt; cuentas &lt;span class="caps"&gt;SET&lt;/span&gt; balance=&lt;/span&gt;&lt;span class="si"&gt;$balance&lt;/span&gt;&lt;span class="s2"&gt;+10 &lt;span class="caps"&gt;WHERE&lt;/span&gt; cuenta=X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;
&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resultado&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt; 7 &lt;/span&gt;    &lt;span class="c1"&gt;// la consulta fue exitosa
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;span class="caps"&gt;COMMIT&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="ln"&gt;10 &lt;/span&gt;    &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;span class="caps"&gt;ROLLBACK&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;(falta revisar que la consulta que obtiene el balance sea exitosa, pero
de todas formas esto es solo un&amp;nbsp;ejemplo)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="autocommit"&gt;
&lt;h2&gt;autocommit&lt;/h2&gt;
&lt;p&gt;MySQL tiene una variable de entorno llamada &lt;tt class="docutils literal"&gt;autocommit&lt;/tt&gt;, que por defecto
tiene el valor 1. Configurado de esta manera no se pueden usar
transacciones, porque MySQL automáticamente hace un &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COMMIT&lt;/span&gt;&lt;/tt&gt; luego de
cada&amp;nbsp;consulta.&lt;/p&gt;
&lt;p&gt;Para usar transacciones entonces, hay que poner autocommit a 0&amp;nbsp;(desactivarlo).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nota&lt;/strong&gt;: si autocommit se pone a cualquier número N &amp;gt; 1, MySQL hace un
&lt;span class="caps"&gt;COMMIT&lt;/span&gt; automático luego de N&amp;nbsp;consultas.&lt;/p&gt;
&lt;p&gt;Para cambiar el valor de autocommit, simplemente se&amp;nbsp;usa&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;SET&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;autocommit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;He aquí lo que sé sobre las transacciones. Es un tema interesante y
cualquiera que programe un sistema relativamente complejo debería
considerar si necesita transacciones para asegurar la &amp;#8220;salud&amp;#8221; de sus
tablas y&amp;nbsp;datos.&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.databasejournal.com/features/mysql/article.php/3382171"&gt;Transactions in MySQL -&amp;nbsp;DatabaseJournal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.webmonkey.com/webmonkey/backend/databases/tutorials/tutorial2.html"&gt;Transactions in MySQL -&amp;nbsp;Webmonkey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/MySQL"&gt;MySQL -&amp;nbsp;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/ACID"&gt;&lt;span class="caps"&gt;ACID&lt;/span&gt; -&amp;nbsp;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/MyISAM"&gt;MyISAM -&amp;nbsp;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/InnoDB"&gt;InnoDB -&amp;nbsp;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/Transacción_(base_de_datos)"&gt;Transacción -&amp;nbsp;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Como lamentablemente suele suceder, las versiones en inglés de los
artículos de Wikipedia contienen información más detallada que las
versiones en&amp;nbsp;español.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sat, 02 Jan 2010 19:11:36 -0200</pubDate><guid>tag:blog.elcodiguero.com,2010-01-02:mysql/transacciones-en-mysql.html</guid><category>MySQL</category></item><item><title>Manejando archivos .INI con PHP</title><link>http://blog.elcodiguero.com/php/manejando-archivos-ini-con-php.html</link><description>&lt;div class="section" id="que-es-un-archivo-ini"&gt;
&lt;h2&gt;¿Qué es un archivo&amp;nbsp;.&lt;span class="caps"&gt;INI&lt;/span&gt;?&lt;/h2&gt;
&lt;p&gt;Se puede leer una &lt;a class="reference external" href="http://es.wikipedia.org/wiki/.ini"&gt;descripción del formato &lt;span class="caps"&gt;INI&lt;/span&gt;&lt;/a&gt; en la&amp;nbsp;Wikipedia.&lt;/p&gt;
&lt;p&gt;Puesto de forma sencilla, un archivo &lt;span class="caps"&gt;INI&lt;/span&gt; es un archivo de texto con un
formato bastante simple: cada línea es de la&amp;nbsp;forma&lt;/p&gt;
&lt;pre class="literal-block"&gt;
campo = &amp;quot;valor&amp;quot;
&lt;/pre&gt;
&lt;p&gt;con posibilidad de contener secciones, declaradas con una línea
&lt;tt class="docutils literal"&gt;[nombre_sección]&lt;/tt&gt;. Son archivos bastante simples y pueden llegar a
ser útiles, aunque no sean demasiado&amp;nbsp;flexibles.&lt;/p&gt;
&lt;p&gt;Un ejemplo de archivo &lt;span class="caps"&gt;INI&lt;/span&gt; es el archivo de configuración de la base de
datos para &lt;a class="reference external" href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[base_de_datos]
db.adapter = PDO_MYSQL
db.config.host = servidor
db.config.username = usuario
db.config.password = contraseña
db.config.dbname = base
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="como-leer-un-archivo-ini"&gt;
&lt;h2&gt;¿Cómo leer un archivo&amp;nbsp;&lt;span class="caps"&gt;INI&lt;/span&gt;?&lt;/h2&gt;
&lt;p&gt;Los archivos &lt;span class="caps"&gt;INI&lt;/span&gt; pueden ser transformados en matrices de &lt;span class="caps"&gt;PHP&lt;/span&gt; usando la
función &lt;a class="reference external" href="http://www.php.net/parse_ini_file"&gt;parse_ini_file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Esta función admite 2 parámetros: El primero es el nombre del archivo a
procesar y el segundo, opcional, indica si se deben procesar las
secciones del&amp;nbsp;archivo.&lt;/p&gt;
&lt;p&gt;Por defecto las secciones no se procesan. La diferencia entre ambas
formas es que si se procesan las secciones se genera una matriz
multidimensional, una matriz para cada&amp;nbsp;sección.&lt;/p&gt;
&lt;p&gt;Por ejemplo, aplicar la función al archivo de configuración mostrado&amp;nbsp;antes:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nb"&gt;var_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;parse_ini_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'archivo.ini'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Produce la siguiente&amp;nbsp;salida:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
array(5) {
    [&amp;quot;db.adapter&amp;quot;]=&amp;gt;
        string(9) &amp;quot;PDO_MYSQL&amp;quot;
    [&amp;quot;db.config.host&amp;quot;]=&amp;gt;
        string(9) &amp;quot;servidor&amp;quot;
    [&amp;quot;db.config.username&amp;quot;]=&amp;gt;
        string(4) &amp;quot;usuario&amp;quot;
    [&amp;quot;db.config.password&amp;quot;]=&amp;gt;
        string(0) &amp;quot;contraseña&amp;quot;
    [&amp;quot;db.config.dbname&amp;quot;]=&amp;gt;
        string(6) &amp;quot;base&amp;quot;
    }
&lt;/pre&gt;
&lt;p&gt;Mientras que aplicar la función indicando que deben procesarse las&amp;nbsp;secciones:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nb"&gt;var_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;parse_ini_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'archivo.ini'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Devuelve lo&amp;nbsp;siguiente:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
array(1) {
    [&amp;quot;base_de_datos&amp;quot;]=&amp;gt;
        array(5) {
            [&amp;quot;db.adapter&amp;quot;]=&amp;gt;
                string(9) &amp;quot;PDO_MYSQL&amp;quot;
            [&amp;quot;db.config.host&amp;quot;]=&amp;gt;
                string(9) &amp;quot;servidor&amp;quot;
            [&amp;quot;db.config.username&amp;quot;]=&amp;gt;
                string(4) &amp;quot;usuario&amp;quot;
            [&amp;quot;db.config.password&amp;quot;]=&amp;gt;
                string(0) &amp;quot;contraseña&amp;quot;
            [&amp;quot;db.config.dbname&amp;quot;]=&amp;gt;
                string(6) &amp;quot;base&amp;quot;
        }
    }
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="como-escribir-un-archivo-ini"&gt;
&lt;h2&gt;¿Cómo escribir un archivo&amp;nbsp;&lt;span class="caps"&gt;INI&lt;/span&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt; no tiene integrada una función para realizar el proceso inverso, es
decir, convertir una matriz en un archivo &lt;span class="caps"&gt;INI&lt;/span&gt;. Es extraño, porque en
realidad es un proceso muy simple, si se hace uso de &lt;tt class="docutils literal"&gt;foreach&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Este fragmento de código es todo lo que se necesita para generar el&amp;nbsp;archivo:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$clave&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$valor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;    &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="nv"&gt;$clave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' = &amp;quot;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$valor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;5 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Para cada uno de los elementos de la matriz, se escribe la clave, un
signo de igualdad (&amp;#8220;=&amp;#8221;) y el valor entre&amp;nbsp;comillas.&lt;/p&gt;
&lt;p&gt;No es necesario que el valor vaya entre comillas, pero &amp;#8220;no molesta&amp;#8221; y
además sí es necesario si el valor tendrá más de una línea. La constante
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/tt&gt; guarda el valor correcto para el salto de línea (&amp;#8220;\n&amp;#8221; o&amp;nbsp;&amp;#8221;\r\n&amp;#8221;).&lt;/p&gt;
&lt;p&gt;Para escribir las diferentes secciones, se asume que se trata con una
matriz multidimensional. Entonces, simplemente se recorre la primera
matriz, y para cada uno de sus&amp;nbsp;elementos:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Se escribe la clave entre&amp;nbsp;[]&lt;/li&gt;
&lt;li&gt;Se recorre la matriz que contiene, con el mismo código&amp;nbsp;anterior&lt;/li&gt;
&lt;li&gt;Se escribe un salto de línea más. No estoy seguro de que sea
necesario para separar secciones, pero sí que resulta en un archivo
más prolijo y&amp;nbsp;legible.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$clave&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$matriz_interior&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;    &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="s1"&gt;'['&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$clave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;']'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;
&lt;span class="ln"&gt; 6 &lt;/span&gt;    &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz_interior&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$clave2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$valor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt; 7 &lt;/span&gt;        &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="nv"&gt;$clave2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' = &amp;quot;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$valor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt; 8 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt; 9 &lt;/span&gt;
&lt;span class="ln"&gt;10 &lt;/span&gt;    &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Finalmente, solo queda encargarse de cómo se abrirá el archivo y se
guardarán los&amp;nbsp;datos.&lt;/p&gt;
&lt;p&gt;En mi función &lt;tt class="docutils literal"&gt;escribe_ini&lt;/tt&gt;, si se pasa una matriz unidimensional para
guardar, la misma se copia en otra, de forma de trabajar siempre con una
matriz multidimensional. Creo que es la mejor manera de no repetir&amp;nbsp;código.&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escribe_ini&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$archivo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$multi_secciones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$modo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;    &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;    &lt;span class="c1"&gt;# saltos de línea (usar &amp;quot;\r\n&amp;quot; para Windows)
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;span class="caps"&gt;SALTO&lt;/span&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt; 7 &lt;/span&gt;
&lt;span class="ln"&gt; 8 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt; 9 &lt;/span&gt;        &lt;span class="nv"&gt;$tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$matriz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;10 &lt;/span&gt;        &lt;span class="nv"&gt;$matriz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'tmp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$tmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# no importa el nombre de la sección, no se usará
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tmp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;12 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;13 &lt;/span&gt;
&lt;span class="ln"&gt;14 &lt;/span&gt;    &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$clave&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$matriz_interior&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;15 &lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_secciones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;16 &lt;/span&gt;            &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="s1"&gt;'['&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$clave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;']'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;17 &lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;18 &lt;/span&gt;
&lt;span class="ln"&gt;19 &lt;/span&gt;        &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matriz_interior&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$clave2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$valor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt;20 &lt;/span&gt;            &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="nv"&gt;$clave2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' = &amp;quot;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$valor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;21 &lt;/span&gt;
&lt;span class="ln"&gt;22 &lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multi_secciones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;23 &lt;/span&gt;            &lt;span class="nv"&gt;$salida&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="nx"&gt;&lt;span class="caps"&gt;SALTO&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;24 &lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;25 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;26 &lt;/span&gt;
&lt;span class="ln"&gt;27 &lt;/span&gt;    &lt;span class="nv"&gt;$puntero_archivo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$archivo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$modo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;28 &lt;/span&gt;
&lt;span class="ln"&gt;29 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$puntero_archivo&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;30 &lt;/span&gt;        &lt;span class="nv"&gt;$escribo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$puntero_archivo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$salida&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;31 &lt;/span&gt;
&lt;span class="ln"&gt;32 &lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$escribo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;33 &lt;/span&gt;            &lt;span class="nv"&gt;$devolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;34 &lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;35 &lt;/span&gt;            &lt;span class="nv"&gt;$devolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$escribo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;36 &lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;37 &lt;/span&gt;
&lt;span class="ln"&gt;38 &lt;/span&gt;        &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$puntero_archivo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;39 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt;40 &lt;/span&gt;        &lt;span class="nv"&gt;$devolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;41 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;42 &lt;/span&gt;
&lt;span class="ln"&gt;43 &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$devolver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;44 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Los parámetros que recibe&amp;nbsp;son:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;$matriz&lt;/tt&gt; - La matriz que se escribirá en el&amp;nbsp;archivo&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;$archivo&lt;/tt&gt; - La ruta al archivo que se escribirá /&amp;nbsp;creará&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;$multi_secciones&lt;/tt&gt; - Indica si se deben crear secciones o&amp;nbsp;no.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;$modo&lt;/tt&gt; - Se pasa directamente a &lt;strong&gt;fopen()&lt;/strong&gt;, indica cómo se abre el&amp;nbsp;archivo.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Y los valores de&amp;nbsp;retorno:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;-1&lt;/strong&gt;: Error al abrir el archivo para escritura. El motivo más
frecuente es que el usuario no tiene permisos para escribir en el&amp;nbsp;directorio.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-2&lt;/strong&gt;: Error al escribir en el&amp;nbsp;archivo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;otro valor&lt;/strong&gt;: el número de bytes escrito por &lt;a class="reference external" href="http://www.php.net/fwrite"&gt;fwrite()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/.ini"&gt;Wikipedia:&amp;nbsp;.ini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/parse_ini_file"&gt;Manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;parse_ini_file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/manual/es/ref.filesystem.php"&gt;Manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;: funciones del sistema de&amp;nbsp;archivos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 31 Dec 2009 21:31:03 -0200</pubDate><guid>tag:blog.elcodiguero.com,2009-12-31:php/manejando-archivos-ini-con-php.html</guid><category>PHP</category></item><item><title>Páginas de error HTTP</title><link>http://blog.elcodiguero.com/php/paginas-de-error-http.html</link><description>&lt;p&gt;Los errores &lt;span class="caps"&gt;HTTP&lt;/span&gt; se ven bastante seguido, pueden suceder en cualquier
sitio, en cualquier momento.
El sitio falla, alguien copia mal un enlace que le pasa a un amigo,
alguien escribe mal la dirección a la que quiere ir. Incluso puede pasar
que un motor de búsqueda tenga un enlace viejo en su&amp;nbsp;índice.&lt;/p&gt;
&lt;p&gt;Se podría decir que estos errores son inevitables, pero eso no es excusa
para no saber manejarlos. Un estudio reciente concluyó que el 18% de las
personas que ven una página de error 404 cree que se trata de un error
debido a que el sitio está mal hecho. ¿Volverías a un sitio que
consideras &lt;em&gt;mal hecho&lt;/em&gt; si tuvieras una&amp;nbsp;alternativa?&lt;/p&gt;
&lt;p&gt;Es muy importante saber qué poner en una página de error, cómo tratar al
visitante e intentar retenerlo, y cómo configurar el servidor para que
muestre la página que uno quiere que se muestre, en vez de la fea página
por&amp;nbsp;defecto.&lt;/p&gt;
&lt;p&gt;Hace poco se hizo un &lt;a class="reference external" href="http://www.maestrosdelweb.com/editorial/infoerror/"&gt;artículo sobre el tema&lt;/a&gt; en &lt;a class="reference external" href="http://www.maestrosdelweb.com"&gt;MaestrosdelWeb&lt;/a&gt;.
En este artículo se mencionan algunas instrucciones a tener en cuenta
para crear una página de error que sirva a los intereses del sitio. Es
decir, ya que el error se produce y no lo podemos evitar, debemos
intentar que la página de error sea útil al visitante que se la&amp;nbsp;encuentra.&lt;/p&gt;
&lt;p&gt;Pero el error 404 no es el único que existe (de hecho, en la mayoría de
los casos y según la norma &lt;span class="caps"&gt;HTTP&lt;/span&gt;, debería usarse 410 y no 404)
Hay otros errores comunes que pueden darse en casos de restricciones de
páginas (403 Forbidden) o errores del servidor (500 Internal Server&amp;nbsp;Error).&lt;/p&gt;
&lt;div class="section" id="como-se-hace-para-especificar-una-pagina-diferente-a-la-pagina-por-defecto"&gt;
&lt;h2&gt;¿Cómo se hace para especificar una página diferente a la página por&amp;nbsp;defecto?&lt;/h2&gt;
&lt;p&gt;La forma más sencilla en servidores Apache (la mayoría) es editar el
archivo &lt;a class="reference external" href="http://en.wikipedia.org/wiki/.htaccess"&gt;.htaccess&lt;/a&gt; ([punto]htaccess) de la raíz del&amp;nbsp;sitio.&lt;/p&gt;
&lt;p&gt;Este archivo puede ubicarse en cualquier carpeta, pero hay que tener en
cuenta que las directivas que contenga solo se aplicarán a la carpeta
que lo contiene, y a sus subcarpetas.
Por lo tanto, si se cambian las páginas de error en un archivo .htaccess
ubicado dentro de una carpeta cualquiera, solamente se estarán
redirigiendo los errores que ocurran en archivos dentro de esa&amp;nbsp;carpeta.&lt;/p&gt;
&lt;p&gt;La forma de especificar una página &amp;#8220;personalizada&amp;#8221; para los errores es
la&amp;nbsp;siguiente:&lt;/p&gt;
&lt;pre class="code apache literal-block"&gt;
&lt;span class="nb"&gt;ErrorDocument&lt;/span&gt; [código de &lt;span class="k"&gt;error&lt;/span&gt;] [ruta absoluta a la página que se quiere usar]
&lt;/pre&gt;
&lt;p&gt;Por&amp;nbsp;ejemplo,&lt;/p&gt;
&lt;pre class="code apache literal-block"&gt;
&lt;span class="nb"&gt;ErrorDocument&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; &lt;span class="sx"&gt;/error404.htmlErrorDocument&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="sx"&gt;/error500.html&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Puede usarse la misma página para todos los errores, aunque es
recomendable especificar lo que sucedió: no es lo mismo un error
temporal (la base de datos no está disponible, por ejemplo), que un
error permanente como el 404.
También es importante crear y lanzar errores que correspondan con el
error real: un problema al conectar con la base de datos no provocará
por si mismo un error 500 (&amp;#8220;Error interno del servidor&amp;#8221;) pero si
encontramos y detectamos un problema como este, mostrar una página de
error 500 puede ser lo apropiado, después de todo sí se produjo un error&amp;nbsp;&amp;#8220;interno&amp;#8221;.&lt;/p&gt;
&lt;p&gt;A la hora de personalizar las páginas de error no solo se pueden usar
documentos estáticos, sino que se puede usar también un programa &lt;span class="caps"&gt;PHP&lt;/span&gt; (o
en el lenguaje que sea).
El servidor Apache pone a disposición de este programa las variables
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$\_SERVER[&amp;#8216;&lt;span class="caps"&gt;REDIRECT&lt;/span&gt;\_URI&amp;#8217;]&lt;/span&gt;&lt;/tt&gt; y &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$\_SERVER[&amp;#8216;&lt;span class="caps"&gt;REDIRECT&lt;/span&gt;\_STATUS&amp;#8217;]&lt;/span&gt;&lt;/tt&gt;, que indican
respectivamente la ruta que se envió y que causó el error, y el código
del error que se&amp;nbsp;produjo.&lt;/p&gt;
&lt;p&gt;Por ejemplo, para generar el&amp;nbsp;famoso&lt;/p&gt;
&lt;blockquote&gt;
Error 404: No se pudo encontrar el archivo &lt;span class="caps"&gt;XXX&lt;/span&gt; en este sitio&lt;/blockquote&gt;
&lt;p&gt;Se podría hacer lo siguiente con&amp;nbsp;&lt;span class="caps"&gt;PHP&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Error '&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REDIRECT_STATUS'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;':No se pudo encontrar el archivo '&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REDIRECT_URI'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' en este sitio'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Según la &lt;a class="reference external" href="http://httpd.apache.org/docs/1.3/custom-error.html"&gt;documentación de Apache&lt;/a&gt;, también debe enviarse desde la
página &lt;span class="caps"&gt;PHP&lt;/span&gt; al navegador una cabecera &lt;span class="caps"&gt;HTTP&lt;/span&gt; indicando el error, para
asegurar que el navegador &amp;#8220;se entere&amp;#8221; de lo que realmente sucedió.
Esto también se menciona en la página del manual de &lt;a class="reference external" href="http://www.php.net/header"&gt;header()&lt;/a&gt;, que es
la función de &lt;span class="caps"&gt;PHP&lt;/span&gt; que realiza el trabajo de enviar cabeceras &lt;span class="caps"&gt;HTTP&lt;/span&gt; al&amp;nbsp;navegador.&lt;/p&gt;
&lt;p&gt;Hay que tener en cuenta un detalle más: &lt;span class="caps"&gt;PHP&lt;/span&gt; puede funcionar en el
servidor como módulo de Apache o como &lt;span class="caps"&gt;CGI&lt;/span&gt;.
La información de cómo está funcionando &lt;span class="caps"&gt;PHP&lt;/span&gt; puede obtenerse ejecutando
la función &lt;a class="reference external" href="http://www.php.net/phpinfo"&gt;phpinfo()&lt;/a&gt;, y viendo la salida de &amp;#8220;&lt;em&gt;Server &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/em&gt;&amp;#8220;. Si pone
&amp;#8220;&lt;em&gt;&lt;span class="caps"&gt;CGI&lt;/span&gt;&lt;/em&gt;&amp;#8220;, &lt;span class="caps"&gt;PHP&lt;/span&gt; está funcionando como &lt;span class="caps"&gt;CGI&lt;/span&gt;, mientras que si pone &amp;#8220;&lt;em&gt;Apache
Handler&lt;/em&gt;&amp;#8220;, &lt;span class="caps"&gt;PHP&lt;/span&gt; está funcionando como módulo de&amp;nbsp;Apache.&lt;/p&gt;
&lt;p&gt;Debido a un pequeño fallo de &lt;span class="caps"&gt;PHP&lt;/span&gt;, estas 2 formas de funcionamiento
tienen comportamientos ligeramente diferentes.
En el caso de que &lt;span class="caps"&gt;PHP&lt;/span&gt; funcione como &lt;span class="caps"&gt;CGI&lt;/span&gt;, la cabecera a enviar&amp;nbsp;es:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Status: 404 Not Found'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;mientras que si es&amp;nbsp;módulo:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;?php
header( 'HTTP/1.0 404 Not Found' );
&lt;/pre&gt;
&lt;p&gt;Como se ve, la diferencia está en que una versión usa &lt;tt class="docutils literal"&gt;Status:&lt;/tt&gt;
mientras que la otra usa &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt;/1.0&lt;/tt&gt; (o &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt;/1.1&lt;/tt&gt;, con algunas
diferencias).
Esta diferencia no solamente se da con el error 404, sino también con el
resto de los errores&amp;nbsp;&lt;span class="caps"&gt;HTTP&lt;/span&gt;.&lt;/p&gt;
&lt;div class="section" id="parametros-de-header"&gt;
&lt;h3&gt;Parámetros de&amp;nbsp;header()&lt;/h3&gt;
&lt;p&gt;La función &lt;tt class="docutils literal"&gt;header&lt;/tt&gt; tiene 2 parámetros opcionales, que pueden servir
para evitar las diferencias de funcionamiento entre las versiones de&amp;nbsp;&lt;span class="caps"&gt;PHP&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Según el manual oficial, la sintaxis de la función&amp;nbsp;es&lt;/p&gt;
&lt;pre class="literal-block"&gt;
void header ( string $string [, bool $replace [, int $http_response_code ]] )
&lt;/pre&gt;
&lt;p&gt;El primer parámetro, &lt;tt class="docutils literal"&gt;$string&lt;/tt&gt;, es el que usamos siempre: una cadena con
el contenido de la cabecera.
El segundo, &lt;tt class="docutils literal"&gt;$replace&lt;/tt&gt;, indica si la cabecera enviada con &lt;tt class="docutils literal"&gt;header()&lt;/tt&gt; debe
sustituir a otra cabecera previa con el mismo nombre.
Y el tercero e interesante, &lt;tt class="docutils literal"&gt;$http\_response\_code&lt;/tt&gt;, indica el código
de respuesta &lt;span class="caps"&gt;HTTP&lt;/span&gt; que estará asociado a la cabecera enviada.
Por lo que, según pude leer en las notas de usuario del manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;,
en vez de usar &lt;tt class="docutils literal"&gt;header&lt;/tt&gt; como siempre, se puede evitar el problema de
saber cómo se ejecuta &lt;span class="caps"&gt;PHP&lt;/span&gt; usando&amp;nbsp;simplemente:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Según la persona que escribió la nota, el primer parámetro de la función
no debe ser una cadena vacía, de ahí el uso de un espacio.
Y en mis pruebas, esto genera una cabecera de error perfectamente&amp;nbsp;válida.&lt;/p&gt;
&lt;p&gt;Vale la pena leer las notas del manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;, muchas veces se
encuentran cosas muy interesantes. Y vale la pena también pensar un poco
fuera de lo normal para encontrar soluciones prácticas a los problemas
de todos los&amp;nbsp;días.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.maestrosdelweb.com/editorial/infoerror/"&gt;MaestrosdelWeb: Usabilidad en errores&amp;nbsp;404&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.faqs.org/rfcs/rfc2616"&gt;Especificación &lt;span class="caps"&gt;HTTP&lt;/span&gt; 1.1 (detalles del protocolo y descripción de los
códigos de&amp;nbsp;respuesta)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/.htaccess"&gt;Wikipedia:&amp;nbsp;.htaccess&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://httpd.apache.org/docs/1.3/custom-error.html"&gt;Documentación de Apache: errores&amp;nbsp;personalizados&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/header"&gt;Manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;header&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/phpinfo"&gt;Manual de &lt;span class="caps"&gt;PHP&lt;/span&gt;:&amp;nbsp;phpinfo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 31 Dec 2009 21:19:01 -0200</pubDate><guid>tag:blog.elcodiguero.com,2009-12-31:php/paginas-de-error-http.html</guid><category>PHP</category></item><item><title>Colores predefinidos en CSS</title><link>http://blog.elcodiguero.com/css/colores-predefinidos-en-css.html</link><description>&lt;p&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; provee varias formas de definir colores como valores de propiedades
(fondo, color de letra, bordes, etc). Estas&amp;nbsp;son:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;rgb(&lt;span class="caps"&gt;RRR&lt;/span&gt;,&lt;span class="caps"&gt;GGG&lt;/span&gt;,&lt;span class="caps"&gt;BBB&lt;/span&gt;)&lt;/dt&gt;
&lt;dd&gt;Valores decimales para rojo, verde y azul, cada uno entre 0 y&amp;nbsp;255.&lt;/dd&gt;
&lt;dt&gt;rgb(&lt;span class="caps"&gt;RR&lt;/span&gt;%, &lt;span class="caps"&gt;GG&lt;/span&gt;%,&amp;nbsp;&lt;span class="caps"&gt;BB&lt;/span&gt;%)&lt;/dt&gt;
&lt;dd&gt;Valores en porcentajes de rojo, verde y azul. Indicar 0% es igual a
poner 0 en la forma anterior, e indicar 100% es lo mismo que poner&amp;nbsp;255.&lt;/dd&gt;
&lt;dt&gt;#&lt;span class="caps"&gt;RRGGBB&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;valores hexadecimales (tal y como se usan en&amp;nbsp;&lt;span class="caps"&gt;HTML&lt;/span&gt;)&lt;/dd&gt;
&lt;dt&gt;#&lt;span class="caps"&gt;RGB&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Valores hexadecimales abreviados. Por ejemplo, #&lt;span class="caps"&gt;FFFFFF&lt;/span&gt; (blanco) se
puede escribir como&amp;nbsp;#&lt;span class="caps"&gt;FFF&lt;/span&gt;&lt;/dd&gt;
&lt;dt&gt;color&lt;/dt&gt;
&lt;dd&gt;Uno de los 16 &lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/syndata.html#color-units"&gt;colores predefinidos por la norma &lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/a&gt;.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Esta última opción es la más cómoda, pero también provoca algunos
problemas: muchas veces uno prueba el nombre de un color cualquiera (por
ejemplo &amp;#8220;skyblue&amp;#8221;, celeste), ve que funciona, y sigue diseñando
tranquilamente. Pero al validar, el validador indica que &amp;#8220;skyblue&amp;#8221; no es
un valor válido&amp;#8230;
Es bueno entonces tener una lista de los colores predefinidos, para
evitar estos&amp;nbsp;detalles.&lt;/p&gt;
&lt;p&gt;Y los colores predefinidos en &lt;span class="caps"&gt;CSS2&lt;/span&gt; son los&amp;nbsp;siguientes:&lt;/p&gt;
&lt;table style="text-align: center"&gt;
    &lt;tr&gt;
        &lt;td style="background: white"&gt;white&lt;/td&gt;
        &lt;td style="background: black; color: white"&gt;black&lt;/td&gt;
        &lt;td style="background: aqua"&gt;aqua&lt;/td&gt;
        &lt;td style="background: blue; color: white"&gt;blue&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td style="background: fuchsia"&gt;fuchsia&lt;/td&gt;
        &lt;td style="background: gray"&gt;gray&lt;/td&gt;
        &lt;td style="background: green"&gt;green&lt;/td&gt;
        &lt;td style="background: lime"&gt;lime&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td style="background: maroon; color: white"&gt;maroon&lt;/td&gt;
        &lt;td style="background: navy; color: white"&gt;navy&lt;/td&gt;
        &lt;td style="background: olive"&gt;olive&lt;/td&gt;
        &lt;td style="background: purple; color: white"&gt;purple&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td style="background: red"&gt;red&lt;/td&gt;
        &lt;td style="background: silver"&gt;silver&lt;/td&gt;
        &lt;td style="background: teal"&gt;teal&lt;/td&gt;
        &lt;td style="background: yellow"&gt;yellow&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;&lt;p&gt;No son muchos, pero son bastante variados. Y siempre es más cómodo
escribir &lt;strong&gt;white&lt;/strong&gt; que &lt;strong&gt;rgb(255,255,255)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;CSS3&lt;/span&gt; promete muchas más opciones a la hora de &lt;a class="reference external" href="http://www.w3.org/TR/css3-color/"&gt;manejar los colores&lt;/a&gt;. Se
podrá elegir el perfil de color, usar los colores predefinidos para &lt;span class="caps"&gt;SVG&lt;/span&gt;,
y algunas otras cosas&amp;nbsp;más.&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h2&gt;Enlaces&amp;nbsp;relacionados&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/cover.html"&gt;&lt;span class="caps"&gt;CSS2&lt;/span&gt;, especificación en&amp;nbsp;español&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/syndata.html#color-units"&gt;&lt;span class="caps"&gt;CSS2&lt;/span&gt;: colores&amp;nbsp;predefinidos.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.w3.org/TR/css3-color/"&gt;&lt;span class="caps"&gt;CSS3&lt;/span&gt; Color&amp;nbsp;Module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 31 Dec 2009 20:48:03 -0200</pubDate><guid>tag:blog.elcodiguero.com,2009-12-31:css/colores-predefinidos-en-css.html</guid><category>CSS</category></item><item><title>Propiedades de fondo en CSS</title><link>http://blog.elcodiguero.com/css/propiedades-de-fondo-en-css.html</link><description>&lt;p&gt;Con &lt;span class="caps"&gt;CSS&lt;/span&gt; se puede especificar el fondo de un elemento cualquiera, y sus
propiedades.
Se puede especificar el color, la imagen que se usará, si esta imagen
debe repetirse o no, y dónde debe&amp;nbsp;ubicarse.&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background-image&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;ruta&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="nt"&gt;la&lt;/span&gt; &lt;span class="nt"&gt;imagen&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;La imagen que se usará como fondo. Casi todos los navegadores actuales
(excepto &lt;span class="caps"&gt;IE&lt;/span&gt; &amp;lt;= 8) soportan múltiples imágenes de fondo, que se
superponen según sea necesario. Es decir, se puede colocar una imagen de
fondo arriba a la derecha, otra abajo a la izquierda, y otra que se
repita en toda la página. La posibilidad de especificar múltiples
imágenes de fondo para un mismo elemento es parte de &lt;span class="caps"&gt;CSS&lt;/span&gt;&amp;nbsp;3.&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background-repeat&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;repeat&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;no-repeat&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;repeat-x&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;repeat-y&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Especifica si la imagen debe repetirse tanto vertical como&amp;nbsp;horizontalmente.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;repeat&lt;/tt&gt; hace que se repita en mosaico en ambos&amp;nbsp;sentidos&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;no-repeat&lt;/span&gt;&lt;/tt&gt; hace que la imagen se muestre solo 1&amp;nbsp;vez&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;repeat-x&lt;/span&gt;&lt;/tt&gt; hace que la imagen se repita&amp;nbsp;horizontalmente&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;repeat-y&lt;/span&gt;&lt;/tt&gt; hace que la imagen se repita&amp;nbsp;verticalmente.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background-color&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;rgb&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;&lt;span class="caps"&gt;XXX&lt;/span&gt;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&lt;span class="caps"&gt;YYY&lt;/span&gt;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&lt;span class="caps"&gt;ZZZ&lt;/span&gt;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;#&lt;span class="caps"&gt;XXYYZZ&lt;/span&gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;color&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Especifica el color del&amp;nbsp;fondo.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;rgb(&lt;span class="caps"&gt;XXX&lt;/span&gt;,&lt;span class="caps"&gt;YYY&lt;/span&gt;,&lt;span class="caps"&gt;ZZZ&lt;/span&gt;)&lt;/tt&gt; Valores decimales para rojo, verde y&amp;nbsp;azul&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;#&lt;span class="caps"&gt;XXYYZZ&lt;/span&gt;&lt;/tt&gt; valores hexadecimales (tal y como se usan en&amp;nbsp;&lt;span class="caps"&gt;HTML&lt;/span&gt;)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;color&lt;/tt&gt; uno de los 17 &lt;a class="reference external" href="/css/colores-predefinidos-en-css.html"&gt;colores predefinidos en &lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background-attachment&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;scroll&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;fixed&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Especifica si la imagen de fondo debe desplazarse o no con el texto
(&lt;tt class="docutils literal"&gt;scroll&lt;/tt&gt; la deja moverse, &lt;tt class="docutils literal"&gt;fixed&lt;/tt&gt; la deja&amp;nbsp;fija).&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;posVertical&lt;/span&gt; &lt;span class="nt"&gt;posHorizontal&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Especifica la posición en la que se ubica la imagen de fondo. La forma
más fácil de especificarla es usando las palabras &lt;tt class="docutils literal"&gt;top&lt;/tt&gt; (arriba),
&lt;tt class="docutils literal"&gt;left&lt;/tt&gt; (izquierda), &lt;tt class="docutils literal"&gt;right&lt;/tt&gt; (derecha) y &lt;tt class="docutils literal"&gt;bottom&lt;/tt&gt; (abajo). También
pueden especificarse porcentajes, por ejemplo un valor de &lt;tt class="docutils literal"&gt;15% 30%&lt;/tt&gt;
ubicará la imagen de fondo (su esquina superior izquierda) con un margen
izquierdo de 15% del ancho de la página, y un margen superior de 30% de
la altura de la página. Otros valores más usuales son, por ejemplo
&lt;tt class="docutils literal"&gt;top left&lt;/tt&gt;, o &lt;tt class="docutils literal"&gt;bottom right&lt;/tt&gt;.&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;La forma corta, personalmente me parece difícil de recordar y más
compleja que usar las propiedades largas, pero existe. Sirve para
especificar todas las propiedades del fondo en una sola&amp;nbsp;línea:&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;background-color&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;background-image&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;background-repeat&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;background-attachment&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;background-position&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;No es necesario especificar todas las propiedades, lo siguiente es
perfectamente&amp;nbsp;válido:&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;white&lt;/span&gt; &lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;fondo&lt;/span&gt;&lt;span class="nc"&gt;.png&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Y tampoco es necesario especificar las propiedades en el orden mostrado,
lo siguiente también es válido (ejemplo mostrado en el sitio del&amp;nbsp;&lt;span class="caps"&gt;W3C&lt;/span&gt;):&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;chess.png&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;gray&lt;/span&gt; &lt;span class="nt"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nt"&gt;repeat&lt;/span&gt; &lt;span class="nt"&gt;fixed&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h2&gt;Enlaces&amp;nbsp;relacionados&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/cover.html"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; Nivel&amp;nbsp;2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/colors.html"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt;: Colores y&amp;nbsp;fondos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/syndata.html#color-units"&gt;Valores de &lt;span class="caps"&gt;CSS&lt;/span&gt;:&amp;nbsp;colores&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="/css/colores-predefinidos-en-css.html"&gt;Colores predefinidos en&amp;nbsp;&lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Thu, 31 Dec 2009 20:27:59 -0200</pubDate><guid>tag:blog.elcodiguero.com,2009-12-31:css/propiedades-de-fondo-en-css.html</guid><category>CSS</category></item><item><title>¿Cómo hacer para ocultar y mostrar un elemento de la página?</title><link>http://blog.elcodiguero.com/css/como-hacer-para-ocultar-y-mostrar-un-elemento-de-la-pagina.html</link><description>&lt;div class="section" id="las-propiedades-display-y-visibility"&gt;
&lt;h2&gt;Las propiedades display y&amp;nbsp;visibility&lt;/h2&gt;
&lt;p&gt;Hacer que un elemento se oculte o se muestre según las acciones del
usuario es un efecto muy práctico y que da una buena sensación de&amp;nbsp;interactividad.&lt;/p&gt;
&lt;p&gt;Para controlar la visibilidad de un elemento se dispone de las
propiedades de &lt;span class="caps"&gt;CSS&lt;/span&gt; &lt;tt class="docutils literal"&gt;display&lt;/tt&gt; y &lt;tt class="docutils literal"&gt;visibility&lt;/tt&gt;. Ambas propiedades
controlan la forma en que se muestra un elemento cualquiera de la&amp;nbsp;página.&lt;/p&gt;
&lt;p&gt;Si se considera el siguiente código&amp;nbsp;&lt;span class="caps"&gt;HTML&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;un_div&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Cambiando el valor de &lt;tt class="docutils literal"&gt;display&lt;/tt&gt; se puede&amp;nbsp;lograr:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Que se vea normalmente como una caja (modo por&amp;nbsp;defecto)&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nf"&gt;#un_div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;display&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Que no se vea ni ocupe espacio en la&amp;nbsp;página:&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nf"&gt;#un_div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;display&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Se puede jugar también con el valor de &lt;tt class="docutils literal"&gt;visibility&lt;/tt&gt;. Esta propiedad
controla si el elemento se ve o se vuelve transparente. No se puede
decir que lo oculte, porque seguirá ocupando espacio normalmente. Esta
es la principal diferencia con &lt;tt class="docutils literal"&gt;display&lt;/tt&gt;, ya que un elemento oculto
mediante ésta última propiedad no ocupa espacio en la página.
En realidad la diferencia es bastante mayor, ya que &lt;tt class="docutils literal"&gt;display&lt;/tt&gt; tiene el
control de cómo se muestra el elemento (como una caja, como un elemento
en línea, como una tabla, como una fila de tabla,&amp;nbsp;etc).&lt;/p&gt;
&lt;p&gt;Se puede ver un ejemplo del funcionamiento de ambas propiedades en &lt;a class="reference external" href="/archivos/mostrar_ocultar_elemento/ejemplo.html"&gt;esta
página de ejemplo&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="controlar-la-visibilidad-con-javascript"&gt;
&lt;h2&gt;Controlar la visibilidad con&amp;nbsp;Javascript&lt;/h2&gt;
&lt;p&gt;Supongamos que el &amp;#8220;div&amp;#8221; mostrado antes está oculto por&amp;nbsp;&lt;span class="caps"&gt;CSS&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="nf"&gt;#un_div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;display&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;El primer paso es obtener el nodo del div en una&amp;nbsp;variable:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;un_div&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Luego, acceder a los estilos del elemento con el atributo &lt;tt class="docutils literal"&gt;style&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;block&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Y listo, aparece el div por arte de MagiaScript(tm)&amp;nbsp;:-P&lt;/p&gt;
&lt;p&gt;Para ocultarlo basta poner &lt;tt class="docutils literal"&gt;display&lt;/tt&gt; a &lt;tt class="docutils literal"&gt;none&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;none&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Si se prefiere usar &lt;tt class="docutils literal"&gt;visibility&lt;/tt&gt;, el procedimiento es idéntico, solo
que se usarán el valor &lt;tt class="docutils literal"&gt;visible&lt;/tt&gt; para que el elemento se vea, y
&lt;tt class="docutils literal"&gt;hidden&lt;/tt&gt; para que se&amp;nbsp;oculte.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Notas&lt;/strong&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;No es necesario guardar el nodo en una variable, pero es más rápido
hacerlo si se deben hacer más modificaciones en&amp;nbsp;él.&lt;/li&gt;
&lt;li&gt;Esto funciona de la misma forma en cualquier elemento de la página,
no es necesario que sea un&amp;nbsp;div&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/visuren.html#display-prop"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt;: La propiedad&amp;nbsp;display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.sidar.org/recur/desdi/traduc/es/css/visufx.html#propdef-visibility"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt;: La propiedad&amp;nbsp;visibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://developer.mozilla.org/en/docs/DOM:document.getElementById"&gt;&lt;span class="caps"&gt;MDC&lt;/span&gt;:&amp;nbsp;document.getElementById&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sat, 26 Dec 2009 17:03:23 -0200</pubDate><guid>tag:blog.elcodiguero.com,2009-12-26:css/como-hacer-para-ocultar-y-mostrar-un-elemento-de-la-pagina.html</guid><category>CSS</category><category>Javascript</category></item><item><title>Registros aleatorios con PHP y MySQL</title><link>http://blog.elcodiguero.com/mysql/registros-aleatorios-php-mysql.html</link><description>&lt;div class="section" id="el-problema"&gt;
&lt;h2&gt;El&amp;nbsp;problema&lt;/h2&gt;
&lt;p&gt;Obtener un registro al azar de una tabla es útil en diversas ocasiones,
por ejemplo para mostrar imágenes o anuncios diferentes en cada carga de
una página. Sin embargo no existe una forma eficiente de lograr esto en
los motores de bases de datos, al menos no en &lt;a class="reference external" href="http://www.mysql.com"&gt;MySQL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A continuación se describen varios métodos que encontré buscando en
Internet sobre este&amp;nbsp;tema.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="metodo-1-order-by-rand"&gt;
&lt;h2&gt;Método 1: &lt;span class="caps"&gt;ORDER&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;&amp;nbsp;&lt;span class="caps"&gt;RAND&lt;/span&gt;()&lt;/h2&gt;
&lt;p&gt;Esta es la forma ampliamente usada, se podría decir que es una solución
normal para el problema. Sin embargo, en la mayoría de los casos, es la
forma más lenta y menos eficiente de hacerlo.
En la &lt;a class="reference external" href="http://dev.mysql.com/doc/refman/4.1/en/mathematical-functions.html#function_rand"&gt;página del manual de MySQL sobre esta función&lt;/a&gt;, puede&amp;nbsp;leerse:&lt;/p&gt;
&lt;blockquote&gt;
Note that &lt;span class="caps"&gt;RAND&lt;/span&gt;() in a &lt;span class="caps"&gt;WHERE&lt;/span&gt; clause is re-evaluated every time the
&lt;span class="caps"&gt;WHERE&lt;/span&gt; is executed.&lt;/blockquote&gt;
&lt;p&gt;Eso quiere decir que para cada registro que cumple con las cláusulas
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/tt&gt;, se ejecuta una vez la función &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;RAND&lt;/span&gt;()&lt;/tt&gt;. Luego, cuando se
tienen todos los registros, MySQL los ordena según el valor que &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;RAND&lt;/span&gt;&lt;/tt&gt;
devolvió para cada uno.
Cuando se usa junto a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;LIMIT&lt;/span&gt;&lt;/tt&gt;, entra en juego lo que se dice en la
página de &lt;a class="reference external" href="http://dev.mysql.com/doc/refman/5.1/en/limit-optimization.html"&gt;optimización de &lt;span class="caps"&gt;LIMIT&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
If you use &lt;span class="caps"&gt;LIMIT&lt;/span&gt; row_count with &lt;span class="caps"&gt;ORDER&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;, MySQL ends the sorting as
soon as it has found the first row_count rows of the sorted result,
rather than sorting the entire result. If ordering is done by using
an index, this is very fast. If a filesort must be done, all rows
that match the query without the &lt;span class="caps"&gt;LIMIT&lt;/span&gt; clause must be selected, and
most or all of them must be sorted, before it can be ascertained
that the first row_count rows have been found. In either case,
after the initial rows have been found, there is no need to sort any
remainder of the result set, and MySQL does not do so.&lt;/blockquote&gt;
&lt;p&gt;Lo que esto quiere decir es que MySQL detendrá el proceso de selección y
orden de las filas en cuanto encuentra la cantidad de filas pedidas,
sobretodo si la consulta indica buscar y ordenar por un campo índice.
Pero si necesita hacer un orden extra (como cuando se usa &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;RAND&lt;/span&gt;()&lt;/tt&gt;) no
podrá hacer esta optimización: deberá procesar todas las filas de la
tabla y ordenar luego, para finalmente devolver el número de filas&amp;nbsp;pedidas.&lt;/p&gt;
&lt;p&gt;El texto indica también que MySQL ordena solamente hasta encontrar los
primeros row_count resultados (es decir, el número de resultados
pedidos en la consulta). Si bien esto es una mejora de rendimiento, en
general se cumple lo que la página dice, most or all of them must be
sorted: deben ordenarse todos o la mayoría de los&amp;nbsp;registros.&lt;/p&gt;
&lt;p&gt;En tablas grandes puede ser muy lento. Por otro lado, en tablas pequeñas
puede no notarse la diferencia. Pero en cualquier caso es útil saber que
no es el método más&amp;nbsp;eficiente.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="metodo-2-usando-count-y-2-consultas"&gt;
&lt;h2&gt;Método 2: Usando &lt;span class="caps"&gt;COUNT&lt;/span&gt; y 2&amp;nbsp;consultas&lt;/h2&gt;
&lt;p&gt;Este método funciona bien cuando se necesita solamente un registro, o se
necesitan &lt;em&gt;n&lt;/em&gt; registros pero no importa si se obtienen consecutivos a
partir de uno al&amp;nbsp;azar.&lt;/p&gt;
&lt;p&gt;Hace uso de &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;()&lt;/tt&gt;, una función que devuelve el número de filas
afectadas por la&amp;nbsp;consulta.&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cantidad_de_filas&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;FROM&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;tabla&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt; &lt;span class="p"&gt;.....&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Con el asterisco, &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;()&lt;/tt&gt; devuelve el número de filas
afectadas. También puede usarse &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;(nombre_de_columna)&lt;/tt&gt;, que
devuelve el número filas afectadas en las que el valor almacenado en
nombre_de_columna no es&amp;nbsp;nulo.&lt;/p&gt;
&lt;p&gt;El valor de cantidad_de_filas se guarda en una variable, por ejemplo
&lt;tt class="docutils literal"&gt;$cantidad&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Luego, se genera un número aleatorio entre 0 y &lt;tt class="docutils literal"&gt;$cantidad&lt;/tt&gt; (ejemplo en
&lt;a class="reference external" href="http://php.net"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$aleatorio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$cantidad&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;(se usa &lt;em&gt;cantidad -1&lt;/em&gt; porque el número de resultados se numera de 0 a
&lt;em&gt;cantidad -1&lt;/em&gt;, en vez de de 1 a &lt;em&gt;cantidad&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Finalmente:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="p"&gt;.....&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;LIMIT&lt;/span&gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;aleatorio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Cuando &lt;em&gt;X&lt;/em&gt; es 1, se devuelve un registro aleatorio. Cuando es mayor que 1,
se devuelven &lt;em&gt;X&lt;/em&gt; registros consecutivos, comenzando desde &lt;tt class="docutils literal"&gt;$aleatorio&lt;/tt&gt;. No
es lo mismo que &lt;em&gt;X&lt;/em&gt; registros aleatorios, pero sigue siendo más rápido que
el método anterior en tablas&amp;nbsp;grandes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cuidado con los casos de borde&lt;/strong&gt;: si la cantidad total de registros es
&lt;tt class="docutils literal"&gt;$resultados&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;$aleatorio&lt;/tt&gt; debe ser menor que &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$resultados-X&lt;/span&gt;&lt;/tt&gt; o el índice
será rebasado y solo se devolverán &lt;tt class="docutils literal"&gt;($resultados - $aleatorio)&lt;/tt&gt; filas.
Por ejemplo, si el máximo &lt;span class="caps"&gt;ID&lt;/span&gt; (&lt;tt class="docutils literal"&gt;$resultados&lt;/tt&gt;) es 50 y la consulta&amp;nbsp;queda&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;LIMIT&lt;/span&gt;&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Los resultados devueltos serán solamente 5. Hay que asegurarse de que el
programa sabe manejar esta&amp;nbsp;situación.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="metodo-3-generando-los-numeros-aleatorios-con-php"&gt;
&lt;h2&gt;Método 3: Generando los números aleatorios con&amp;nbsp;&lt;span class="caps"&gt;PHP&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Este método requiere conocer el mayor &lt;span class="caps"&gt;ID&lt;/span&gt; de la tabla, esto se puede
hacer de 3 formas (al&amp;nbsp;menos):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Forma&amp;nbsp;1:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;MAX&lt;/span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Forma 2 (campos &lt;em&gt;auto increment&lt;/em&gt;):&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;SHOW&lt;/span&gt;&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;TABLE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;&lt;span class="caps"&gt;STATUS&lt;/span&gt;&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;LIKE&lt;/span&gt;&lt;/span&gt; &lt;span class="s1"&gt;'nombre_de_la_tabla'&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Forma&amp;nbsp;3:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;
&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;FROM&lt;/span&gt;&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;
&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt;
&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="n"&gt;TABLE_SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nombreBBDD'&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;AND&lt;/span&gt;&lt;/span&gt; &lt;span class="k"&gt;TABLE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nombreTabla'&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Las formas 2 y 3 solamente funcionan con MySQL (aunque otros motores
tienen métodos equivalentes). Devuelven información administrativa de la
tabla, en el caso de la segunda forma se devolverá un registro en el que
uno de los campos es el valor de &lt;em&gt;auto-increment&lt;/em&gt; que se otorgará al
próximo registro que se ingrese, en la tercera forma se solicita este
valor explícitamente.
Basta reducirlo en 1 para obtener el mayor &lt;span class="caps"&gt;ID&lt;/span&gt; actual. El problema es que
puede ser que este mayor id no exista por haber sido borrado, pero por
contrapartida estos métodos no necesitan leer datos de la tabla, por lo
que son más&amp;nbsp;rápidos.&lt;/p&gt;
&lt;p&gt;Sea cual sea la forma, se guarda el resultado en &lt;tt class="docutils literal"&gt;$max&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Suponiendo que se necesiten &lt;em&gt;X&lt;/em&gt; registros, hay que generar &lt;em&gt;X&lt;/em&gt; números
aleatorios entre 1 y &lt;tt class="docutils literal"&gt;$max&lt;/tt&gt;, para luego juntarlos en una lista separada
por&amp;nbsp;comas:&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="nv"&gt;$aleatorios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$aleatorios&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;    &lt;span class="nv"&gt;$nuevo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt; 6 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$nuevo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$aleatorios&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="ln"&gt; 7 &lt;/span&gt;        &lt;span class="nv"&gt;$aleatorios&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$nuevo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt; 9 &lt;/span&gt;
&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="nv"&gt;$lista&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$aleatorios&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Y finalmente hacer la&amp;nbsp;consulta:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;&lt;span class="caps"&gt;IN&lt;/span&gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;El problema es que puede ser que algunos &lt;span class="caps"&gt;ID&lt;/span&gt; no existan, por haber sido
borrados. Lo que implica que puede suceder que se devuelvan menos de X
registros. La solución a este problema (parcial, ya que aún puede caerse
en un valor faltante) es generar más de &lt;em&gt;X&lt;/em&gt; números (5*X, por ejemplo),
dependiendo de qué tan fragmentada esté la tabla. La probabilidad dice,
sin embargo, que a menos que todos los &lt;span class="caps"&gt;ID&lt;/span&gt; estén en su lugar, o se
generen $max números aleatorios, siempre existe la posibilidad de que se
devuelvan menos de &lt;em&gt;X&lt;/em&gt;&amp;nbsp;registros.&lt;/p&gt;
&lt;p&gt;No estoy seguro de que sea un método realmente más rápido, aunque no es
necesario un orden sí es necesario comparar el &lt;span class="caps"&gt;ID&lt;/span&gt; con cada uno de los X
(o X*N) valores (a menos que se lo encuentre antes, claro). Y sigue
siendo necesario comprobar en cada paso de la generación de la lista,
que el valor no esté en la matriz de&amp;nbsp;valores.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Casi siempre que se pregunta cómo obtener un registro de forma
aleatoria, la respuesta es algo similar al método 1. Que si bien es
simple y directo para el programador, puede hacer que la base de datos
tenga que trabajar mucho más de lo&amp;nbsp;debido.&lt;/p&gt;
&lt;p&gt;En mi opinión, y sin haber hecho pruebas (basicamente por no tener una
tabla real lo suficientemente grande), nada mejor que el 2ª método para
obtener un registro aleatorio.
El 1ª sirve para tablas pequeñas, mientras que el 3ª método es un poco
menos seguro, ya que no siempre devolverá la cantidad de registros
pedida si la tabla está fragmentada (tiene valores borrados) o si se
utiliza una cláusula &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/tt&gt; (que para el caso resulta lo mismo que
tener la tabla fragmentada). Requiere algo más de trabajo con el
lenguaje de programación que se utilice, aunque por otro lado evita
hacer búsquedas y ordenamientos innecesarios en&amp;nbsp;MySQL.&lt;/p&gt;
&lt;p&gt;Tengo entendido que en otros &lt;a class="reference external" href="http://es.wikipedia.org/wiki/SGBD"&gt;&lt;span class="caps"&gt;SGBD&lt;/span&gt;&lt;/a&gt; como &lt;a class="reference external" href="http://www.oracle.com"&gt;Oracle&lt;/a&gt;, existe un índice
numérico y secuencial de todas las filas generado por el gestor. Si
existe esa posibilidad, el método 3 se vuelve ideal y pierde su
desventaja principal. No he podido comprobar si esto mismo existe en
MySQL, y luego de bastantes búsquedas asumo que&amp;nbsp;no.&lt;/p&gt;
&lt;p&gt;Pero más allá de estos truquitos, lo importante es intentar optimizar
las consultas y aprender sobre cómo trabaja el &lt;span class="caps"&gt;SGBD&lt;/span&gt; que se esté usando.
Es imprescindible para una aplicación eficiente generar los índices que
necesite, para evitar que el gestor tenga que buscar en la tabla
completa para encontrar lo que la consulta pide.
El manual de MySQL tiene &lt;a class="reference external" href="http://dev.mysql.com/doc/refman/5.0/es/query-speed.html"&gt;una sección dedicada a esto&lt;/a&gt; que vale la pena&amp;nbsp;leer.&lt;/p&gt;
&lt;div class="section" id="enlaces-relacionados"&gt;
&lt;h3&gt;Enlaces&amp;nbsp;relacionados&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.mysql.com"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://dev.mysql.com/doc/refman/4.1/en/mathematical-functions.html#function_rand"&gt;MySQL: Función&amp;nbsp;&lt;span class="caps"&gt;RAND&lt;/span&gt;()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://dev.mysql.com/doc/refman/5.1/en/limit-optimization.html"&gt;MySQL: Optimización de&amp;nbsp;&lt;span class="caps"&gt;LIMIT&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://dev.mysql.com/doc/refman/5.0/es/query-speed.html"&gt;MySQL: Optimización de&amp;nbsp;consultas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.php.net/rand"&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;: Función&amp;nbsp;rand()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://es.wikipedia.org/wiki/SGBD"&gt;Wikipedia: Sistema de Gestión de Bases de&amp;nbsp;Datos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.tallerwebmaster.com/Tutorial-Registros-aleatorios-con-PHP-y-MySQL-c-132.html"&gt;Una versión anterior de este mismo artículo&lt;/a&gt;, alojada en Taller&amp;nbsp;Webmaster&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="respuestas"&gt;
&lt;h4&gt;Respuestas&lt;/h4&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.forocreativo.net/user/130043-cleptomano/"&gt;cleptomano&lt;/a&gt;, usuario de ForoCreativo, contestó lo siguiente en&amp;nbsp;tallerwebmaster:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hola, como&amp;nbsp;estan&amp;#8230;&lt;/p&gt;
&lt;p&gt;He leido el tutorial y me parece relamente interesante que hayan
tantas formas de obtener un registro&amp;nbsp;aleatorio&lt;/p&gt;
&lt;p&gt;De los tres métodos propuestos, obviamente he descartado la tercera
forma, quedandome solo la primera y la&amp;nbsp;segunda.&lt;/p&gt;
&lt;blockquote&gt;
Método 1: &amp;#8220;&lt;span class="caps"&gt;ORDER&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt; &lt;span class="caps"&gt;RAND&lt;/span&gt;()&amp;#8221;
Esta es la forma ampliamente usada, no solo en MySQL pero en
muchos otros sistemas también. Se podría decir que es una
solución &amp;#8220;normal&amp;#8221; para el problema. Sin embargo, es
probablemente la forma más lenta y menos eficiente de hacerlo.&lt;/blockquote&gt;
&lt;p&gt;Pues no&amp;#8230; no es la más lenta ni la menos eficiente y voy a&amp;nbsp;probarlo&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;    &lt;span class="c1"&gt;// Consulta &lt;span class="caps"&gt;SQL&lt;/span&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="nv"&gt;$sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;SELECT&lt;/span&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="s2"&gt;        c.id_contacto,
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="s2"&gt;        c.email
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;FROM&lt;/span&gt; ts_contactos &lt;span class="caps"&gt;AS&lt;/span&gt; c
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;ORDER&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt; &lt;span class="caps"&gt;RAND&lt;/span&gt;()
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;LIMIT&lt;/span&gt; 1
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="s2"&gt;    &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;14 &lt;/span&gt;
&lt;span class="ln"&gt;15 &lt;/span&gt;    &lt;span class="nv"&gt;$resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;16 &lt;/span&gt;
&lt;span class="ln"&gt;17 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mysql_num_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="ln"&gt;18 &lt;/span&gt;        &lt;span class="nv"&gt;$datos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;19 &lt;/span&gt;        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$datos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;20 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;21 &lt;/span&gt;
&lt;span class="ln"&gt;22 &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;24 &lt;/span&gt;
&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="ln"&gt;26 &lt;/span&gt;    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt;27 &lt;/span&gt;    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;p&amp;gt;Registro &amp;lt;strong&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;28 &lt;/span&gt;    &lt;span class="nb"&gt;print_r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;29 &lt;/span&gt;    &lt;span class="nb"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Registros devueltos en 30 segundos:&amp;nbsp;9&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="ln"&gt; 3 &lt;/span&gt;    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt; 4 &lt;/span&gt;
&lt;span class="ln"&gt; 5 &lt;/span&gt;    &lt;span class="c1"&gt;// Consulta &lt;span class="caps"&gt;SQL&lt;/span&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="nv"&gt;$sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;SELECT&lt;/span&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="s2"&gt;        &lt;span class="caps"&gt;COUNT&lt;/span&gt;(*) &lt;span class="caps"&gt;AS&lt;/span&gt; total
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;FROM&lt;/span&gt; ts_contactos &lt;span class="caps"&gt;AS&lt;/span&gt; c
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="s2"&gt;    &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;11 &lt;/span&gt;
&lt;span class="ln"&gt;12 &lt;/span&gt;    &lt;span class="nv"&gt;$resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;13 &lt;/span&gt;    &lt;span class="nv"&gt;$total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;14 &lt;/span&gt;
&lt;span class="ln"&gt;15 &lt;/span&gt;    &lt;span class="c1"&gt;// Consulta &lt;span class="caps"&gt;SQL&lt;/span&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="nv"&gt;$sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;SELECT&lt;/span&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="s2"&gt;        c.id_contacto,
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="s2"&gt;        c.email
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;FROM&lt;/span&gt; ts_contactos &lt;span class="caps"&gt;AS&lt;/span&gt; c
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="s2"&gt;    &lt;span class="caps"&gt;LIMIT&lt;/span&gt; &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mt_rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;,1
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="s2"&gt;    &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;23 &lt;/span&gt;
&lt;span class="ln"&gt;24 &lt;/span&gt;    &lt;span class="nv"&gt;$resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;25 &lt;/span&gt;
&lt;span class="ln"&gt;26 &lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mysql_num_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="ln"&gt;27 &lt;/span&gt;        &lt;span class="nv"&gt;$datos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mysql_fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;28 &lt;/span&gt;        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$datos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;29 &lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;30 &lt;/span&gt;
&lt;span class="ln"&gt;31 &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="ln"&gt;33 &lt;/span&gt;
&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="ln"&gt;35 &lt;/span&gt;    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt;36 &lt;/span&gt;    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;p&amp;gt;Registro &amp;lt;strong&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="ln"&gt;37 &lt;/span&gt;    &lt;span class="nb"&gt;print_r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="ln"&gt;38 &lt;/span&gt;    &lt;span class="nb"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Registros devueltos en 30 segundos:&amp;nbsp;4&lt;/p&gt;
&lt;p&gt;Las 2 pruebas la hize sobre una tabla con 780mil registros, ahora&amp;#8230;
¿cúal es&amp;nbsp;mejor?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ante esto, mi respuesta&amp;nbsp;fue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hola cleptomano,
Soy el autor del &amp;#8220;tutorial&amp;#8221;, me gusta mucho ver un comentario
fundamentado y que me obliga a investigar más del&amp;nbsp;tema.&lt;/p&gt;
&lt;p&gt;Creo que la frase de &amp;#8220;peor y menos eficiente&amp;#8221; no debería estar ahí,
es una de las cosas que voy a quitar cuando edite el artículo (estoy
pensando en rediseñar mi sitio y reescribir algunas cosas). Estas
cosas siempre dependen de la tabla y de los datos que&amp;nbsp;tenga.&lt;/p&gt;
&lt;p&gt;Me gustaría ver, en realidad, una prueba más: ¿qué pasa si mueves el
&lt;span class="caps"&gt;COUNT&lt;/span&gt;() fuera del bucle? Debería cambiar las&amp;nbsp;cosas&lt;/p&gt;
&lt;p&gt;Otra cosa que he encontrado por ahí, investigando a raíz de tu
comentario, es que las tablas InnoDB no guardan (o al menos, no
guardaban) un número de filas, como sí lo hace MyISAM. Por lo que el
rendimiento en consultas que incluyan &lt;span class="caps"&gt;COUNT&lt;/span&gt; sin &lt;span class="caps"&gt;WHERE&lt;/span&gt; siempre
requiere un &amp;#8216;full table scan&amp;#8217;, lo cual lo hace más lento. ¿Será que
tu tabla es InnoDB?
&lt;a class="reference external" href="http://www.mysqlperformanceblog.com/2006/12/01/count-for-innodb-tables/"&gt;http://www.mysqlperformanceblog.com/2006/12/01/count-for-innodb-tables/&lt;/a&gt;
Este mismo artículo sugiere (en el último comentario) que se puede
forzar a MySQL a utilizar un&amp;nbsp;índice.&lt;/p&gt;
&lt;p&gt;Saludos y gracias de nuevo&amp;nbsp;:-)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alvaro Martínez</dc:creator><pubDate>Sat, 26 Dec 2009 16:45:42 -0200</pubDate><guid>tag:blog.elcodiguero.com,2009-12-26:mysql/registros-aleatorios-php-mysql.html</guid><category>MySQL</category><category>PHP</category></item></channel></rss>