Seamos E_STRICT

PHP tiene un nuevo nivel de error, se llama E_STRICT y está disponible a partir de PHP 5.

Este nuevo nivel de error, nos da mensajes en tiempo de ejecución y nos sugiere cambios en el código para tener un mejor rendimiento además de ayudarnos a tener un código limpio. Veamos un ejemplo típico:

PHP:

<?php

error_reporting = E_ALL | E_STRICT

// Usando la funcion obsoleta 'is_a'
if ( is_a( $objeto, 'Clase' ) ) {
    $objeto->foo();
}
?>
 

E_STRICT nos sugerirá que usemos un operador de tipo, en este caso "instanceof". con lo que tendríamos:

PHP:

<?php
if ( $objeto instanceof Clase ) {
    $objeto->foo();
}
?>
 

Referencias

Instalación del SP1 de Visual Studio 2005

La instalación del SP1 -beta- para Visual Studio 2005 fue realmente un martirio, a continuación una pequeña crónica:

Primero, para poder descargar el SP, tuve que volver a usar -necesariamente- Internet Explorer e instalar un componente ActiveX para la transferencia de archivos, la velocidad de descarga oscilaba en 25 Kb/s, por lo cual tuve que esperar aproximadamente 4 horas para descargar los 371.88 MB del parche principal - me pregunto porque no usan BitTorrent para distribuir este tipo de cosas.

Una vez descargado el SP, tuve esperar unos 5 minutos para que el instalador solamente me pregunte si quiero instalar el parche o no... finalmente, una hora después terminó la instalación y con esto, la actividad de mi procesador volvió a la normalidad.

Unas cuantas imágenes de la actividad que tuvo mi procesador durante la instalación:

No sé si realmente habrá sido problema de mi máquina, o es que talvez no han solucionado completamente los problemas que describían hace tiempo con el tamaño del parche.

Visual Studio 2005 Service Pack 1 Beta

Ya está disponible para la descarga la beta del Service Pack 1 para las versiones en inglés y español del Visual Studio 2005, el tamaño del parche principal es de 371.88 MB

Los parches son los siguientes:

Para descargar este Service Pack, es necesario que se suscriban en la siguiente dirección.

Escribiendo código correcto

Antes de empezar con PHP, escribía mis aplicaciones con ASP 3, utilizando como editor al Visual Interdev, cuando PHP 4 salió a la luz, muchos programadores de ASP se pasaron a PHP, y no sólo ellos sino sus prácticas de programación.

Para esto los desarrolladores de PHP dieron la comodidad a estos "nuevos usuarios" para que sigan con sus malas prácticas de programación y código desordenado, me refiero en especial a la sintaxis para embeber PHP en una página, una de estas es:

PHP:

<?
    echo "Hola Mundo!";
?>
 

¿Qué está mal? Pues la etiqueta <? está reservada para declarar documentos XML y XHTML, debemos olvidarnos de esto definitivamente.

Recordando un poco de ASP, y una de las peores formas de escribir PHP:

PHP:

<?="Hola Mundo!"; ?>
 

¿Qué está mal? Otra vez nos estamos metiendo con XML, si tratamos de mostrar esa página de hecho que tendremos un error, si no es de XML será de PHP.

Y esta que es la peor de todas,

PHP:

<% echo "Hola Mundo!"; %>
 

Este ni hablar, es el estilo de ASP, y recuerdas amigo? ¡ESTAMOS TRABAJANDO CON PHP!

La Solución

Pues es muy fácil, tenemos que escribir el código estándar; es decir:

PHP:

<?php
    echo "Hola Mundo!";
?>
 

Usar la etiqueta de apertura <?php, ¿por qué? pues porque:

  • Se garantiza que cualquier persona entenderá tu código
  • Tus documentos XML serán válidos y se notará la separación con PHP
  • Funciona en todos los documentos PHP

Supongo que alguno me dirá: "Pero Braulio, yo siempre escribo con el método <?= porque es más fácil para pequeñas líneas de código y pierdo menos tiempo" Bueno yo también decía eso... pero lo malo es que a veces lo fácil trae consecuencias catastróficas. Empezemos a ser ordenados desde ahora.

Una pequeña reflexión: "No dije que fuera fácil, dije que valdría la pena."

Referencias

Quiz sobre validación de datos en PHP

El código mostrado a continuación, es una versión reducida de una falla de seguridad presente en una aplicación algo conocida.

Indiquen la falla, lo que se puede hacer con ésta, una forma de explotarlo y la solución que plantean al mismo:

php:
// demo.php
<?php

include './db.php';

error_reporting(0);

$tb_url    = $_POST['url'];
$title     = $_POST['title'];
$excerpt   = $_POST['excerpt'];

if (empty($title) || empty($tb_url) || empty($excerpt)) {
        die ('Invalid values');
}

$titlehtmlspecialchars( strip_tags( $title ) );
$title = (strlen($title) > 150) ? substr($title, 0, 150) . '...' : $title;
$excerpt = strip_tags($excerpt);
$excerpt = (strlen($excerpt) > 200) ? substr($excerpt, 0, 200) . '...' : $excerpt;

$contents=@file_get_contents($tb_url);
if(!$contents) {       
        die('The provided URL does not seem to work.');
}

$query = "INSERT INTO tabla (url, title, excerpt) VALUES ('%s', '%s', '%s')";

$db->query
        (
                sprintf (
                        $query,
                        $db->escape($tb_url),
                        $db->escape($title),
                        $db->escape($excerpt)
                        )
        );

?>
php:
// show.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
        <head>
                <title>Bug</title>
        </head>
        <body>

                <ul>
                <?php

                include './db.php';
                $items = $db->get_results('SELECT url, title, excerpt FROM tabla');

                foreach ($items as $tb) :
                        echo '<li><a href="'.$tb->url.'" title="'.$tb->excerpt.'">'.$tb->title.'</a></li>';               
                endforeach;

                ?>
                </ul>

        </body>
</html>

Para el acceso a datos se usa la clase ez_sql, el método escape en mi versión, contiene lo siguiente:

php:
function escape($string) {
        if (get_magic_quotes_gpc())
                $string = stripslashes($string);
        return mysql_real_escape_string( $string, $this->dbh );
}

Escribiendo código seguro – Más sobre comparación de tipos

Tenemos la siguiente porción de código:

PHP:

<?php
    function foo($respuesta) {
        if ($respuesta > 10) {
            return true;
        } else {
            return $respuesta;
        }
    }
    if (foo(11)) {
        echo "11 es mayor que 10<br />";
    }       
    if (foo(9)) {
        echo "9 es mucho mayor que 10<br />";
    }
?>
 

Lo que hace la función es comparar la variable $respuesta con 10, si es mayor que este entonces devuelve true, caso contrario devuelve el valor de $respuesta. Veamos la salida:

CODE:

11 es mayor que 10
9 es mucho mayor que 10
 

El error está en que PHP toma a cualquier número mayor que cero como un valor verdadero, por eso es que vemos esta salida incoherente.

La Solución

Igual que en la entrada anterior, es mejor usar comparación estricta de tipos, esto es:

PHP:

<?php
    function foo($respuesta) {
        if ($respuesta > 10) {
            return true;
        } else {
            return $respuesta;
        }
    }
    if (foo(11) === true) {
        echo "11 es mayor que 10<br />";
    }       
    if (foo(9) === true) {
        echo "9 es mucho mayor que 10<br />";
    }
?>
 

Ahora si la salida será correcta:

CODE:

11 es mayor que 10
 

Espero sus comentarios y duras, mañana seguiremos con más ;)

Referencias

Escribiendo código seguro – Comparación de Tipos

Cuando en PHP comparamos diferentes variables, pero que en cierto contexto son iguales, los tipos de datos se pierden. Por ejemplo un valor booleano verdadero es representado como 1 y el valor falso es representado como 0. Veamos lo que hace PHP con el siguiente ejemplo:

PHP:

<?php
$entero = 1;
$cadena = "1";
$booleano = true;

var_dump($entero == $cadena);
var_dump($cadena == $booleano);
var_dump($entero == $booleano);
?>
 

En los tres casos se espera "false", pero sin embargo:

CODE:

bool(true) bool(true) bool(true)
 

Otro ejemplo mucho peor:

PHP:

<?php
var_dump('1' == '1.');
?>
 
CODE:

bool(true)
 

En todos los ejemplos hemos usado un "comparador flexible" (==), este solo compara una igualdad. Esto puede traer consecuencias no deseadas en nuestra aplicación, como comenté en la entrada anterior.

La solución

Si queremos código seguro, debemos usar comparadores seguros. Para solucionar nuestro pequeño error, debemos usar un "comparador estricto" (===), este operador sólo dará verdadero si dos variables son idénticas.

PHP:

<?php
$entero = 1;
$cadena = "1";
$booleano = true;

var_dump($entero === $cadena);
var_dump($cadena === $booleano);
var_dump($entero === $booleano);
?>
 

Ahora si la salida será correcta:

CODE:

bool(false) bool(false) bool(false)
 

Una pequeña reflexión: "Así vemos que por medios pequeños, podemos hacer grandes cosas"

Referencias

¿Compuntoes?

Gracias al post que publicó Braulio sobre compuntoes, recién me entero que es un nuevo concurso de posicionamiento, que al parecer ha generado bastante controversia.

Al intentar indagar un poco más en Technorati -que rara vez lo uso- para ver los últimos comentarios sobre el fucking término compuntoes, llego al sitio de uno de los participantes, quien, por el título y el contenido de su última entrada, está realmente descontento por el supuesto tercer lugar que ocupa este blog en la siguiente búsqueda. Lo curioso de esto, es que tiene una linda definición de este blog:

El tercer puesto es un blog cualquiera ahí, salido de las nada con 34 lectores en el feed.

No entiendo porque se preocupa por un blog cualquiera con tan pocos lectores... :P

En fin, suerte a todos participantes de este concurso.

Implementación de un pequeño Servidor Web

El siguiente código, originalmente publicado por Eric Carter, muestra la implementación de un pequeño "Servidor Web" con C#, para hacerlo funcionar necesitarán del .NET Framework 2.0 (podría correr en versiones anteriores haciendo ligeras modificaciones al código).

csharp:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Collections.Specialized;

namespace http
{
    public class FakeWebServer
    {
        private const string URL_REPLACE = "{URL}";
        private static readonly Regex urlRegex = new Regex(@"^(GET|POST) /(.*?) (HTTP[^\s]+)",
                                        RegexOptions.Compiled | RegexOptions.IgnoreCase);

        // Almacena las URLs verdaderas y falsas
        public static readonly Dictionary<string, string> FakeUrls;

        static FakeWebServer()
        {
            FakeUrls = new Dictionary<string, string>();
        }

        private TcpListener listener;
        string contents;

        public FakeWebServer(int port, string responseContents)
        {
            this.contents = responseContents;

            // 'Escuchar' en cualquier dirección
            listener = new TcpListener(IPAddress.Any, port);
            listener.Start();

            Thread t = new Thread(delegate()
            {
                AcceptClients();
            });
            t.Start();
        }
        public void AcceptClients()
        {
            while (true)
            {
                using (TcpClient client = listener.AcceptTcpClient())
                {
                    if (client.Connected) // Nuevo cliente
                    {
                        // Leer los datos enviados
                        NetworkStream stream = client.GetStream();
                        byte[] data = new byte[1024];

                        stream.Read(data, 0, data.Length);

                        string request = Encoding.UTF8.GetString(data);

                        // Sólo tomar en cuenta los datos presentes en el QueryString

                        // Obtener la versión del protocolo y la URL del 'Request'
                        MatchCollection matches = urlRegex.Matches(request);

                        string qs = matches[0].Groups[2].Value.TrimStart('?');
                        NameValueCollection paramArray = HttpUtility.ParseQueryString(qs);

                        foreach (string key in paramArray.AllKeys)
                        {
                            if (FakeUrls.TryGetValue(paramArray[key], out qs))
                                break;
                        }                       
                       
                        System.Diagnostics.Debug.WriteLine("Query String: " + matches[0].Groups[2].Value);

                        // Reemplazar las URLs
                        if (!string.IsNullOrEmpty(qs) && !string.IsNullOrEmpty(contents))
                            contents = contents.Replace(URL_REPLACE, qs);

                        // Enviar las cabeceras necesarias y el contenido
                        SendHeaders(matches[0].Groups[3].Value, null, contents.Length, "200 OK", client);
                        SendToBrowser(Encoding.UTF8.GetBytes(contents), client);
                    }
                }
            }
        }

        public void SendHeaders(string httpVersion, string mimeHeader, int totalBytes, string statusCode, TcpClient tcpClient)
        {
            StringBuilder responseBuilder = new StringBuilder();

            if (string.IsNullOrEmpty(mimeHeader))
                mimeHeader = "text/html";

            responseBuilder.Append(httpVersion);
            responseBuilder.Append(' ');
            responseBuilder.AppendLine(statusCode);
            responseBuilder.AppendLine("Server: Fake Web Server");
            responseBuilder.Append("Content-Type: ");
            responseBuilder.AppendLine(mimeHeader);
            responseBuilder.AppendLine("Accept-Ranges: bytes");
            responseBuilder.Append("Content-Length: ");
            responseBuilder.AppendLine(totalBytes.ToString());
            responseBuilder.AppendLine("");

            Byte[] bSendData = Encoding.UTF8.GetBytes(responseBuilder.ToString());
            SendToBrowser(bSendData, tcpClient);

            System.Diagnostics.Debug.WriteLine("Total Bytes : " + totalBytes.ToString());
        }

        public void SendToBrowser(Byte[] data, TcpClient tcpClient)
        {
            if (tcpClient.Connected)
            {
                NetworkStream stream = tcpClient.GetStream();

                stream.Write(data, 0, data.Length);
                stream.Flush();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Connection Dropped....");
            }

        }
    }
}

Como habrán podido observar el "Servidor Web", entrega tontamente casi el mismo contenido, sólo reemplaza cada aparición de {URL} en la respuesta en base a los parámetros solicitados.

En una siguiente entrada explicaré la valiosa ayuda que presta ese pedazo de código, en la explotación de otro bug de una aplicación ya algo conocida por este blog.

Escribiendo código seguro – Manipulación de Tipos

PHP no soporta la definición explícita de tipos de datos, es decir que cada variable actúa de acuerdo a como es usada. Veamos un ejemplo:

Nota: Para los ejemplos vamos a usar la función var_dump, que nos muestra información acerca de una o mas variables.

PHP:

<?php
$cadena = "1";
$entero = $foo + 1;
var_dump($cadena, $entero);
?>
 

La salida será:

CODE:

string(1) "1" int(2)
 

Como vemos aunque hayamos definido la primera variable como "cadena", al sumarlo con un entero, nos dá otro entero. Para solucionar esto (aunque en realidad no es un problema tan grave), si es que queremos sumar la cadena con un entero, primero deberíamos convertir la cadena a un entero usando casting:

La solución

PHP:

<?php
$cadena = "1";
$entero = (int)$foo + 1;
var_dump((int)$cadena, $entero);
?>
 

La salida será:

CODE:

int(1) int(2)
 

Tratemos de tener siempre en cuenta los tipos de datos que estamos usando, alguien podría aprovechar uno de estos descuidos y podría enviar código malicioso concatenándolo con algún id númerico de nuestra base de datos por dar un ejemplo.

Mañana seguiremos con más ;)

Referencias