Envío masivo de email a lista de contacto con PHPMailer

Seguimos con el PHPMailer tras la introducción y la entrada sobre usar Gmail. Ahora, y recordando un poco el ejemplo de cómo automatizar acciones con cURL, vamos a ver la forma de enviar un correo a una lista de direcciones guardada en una base de datos.

La idea es simple, si recordáis cómo era el script de php de los anteriores ejemplos todo consiste en hacer lo mismo pero con una llamada a la base de datos y un bucle de envíos. En el ejemplo de abajo ya podéis ver el código, explicado punto por punto. en este caso no voy a usar el ejemplo que viene en los docs de la librería, porque está anticuado, usando mysql en lugar de mysqli (es más, creo que lo voy a subir a GitHub):

<?php
//lo primero cargar la librería
require '../PHPMailerAutoload.php';
//lo segundo, crear el objeto mail
$mail = new PHPMailer();
//Vamos a meter el cuerpo (cargado desde un html externo) en una variable
$body = file_get_contents('contents.html');
//definimos el uso de smtp
$mail->isSMTP();
//definimos el servidor que aloja nuestro correo
$mail->Host = 'smtp.tuserver.com';
//activamos la autenticación smtp
$mail->SMTPAuth = true;
//Con esta línea dejamos abierta la conexión al servidor smtp
$mail->SMTPKeepAlive = true; 
//Definimos la seguridad, si nuestro server lo permite lo mejor es usar tls
$mail->SMTPSecure = 'tls';
//el puerto cambia según la seguridad. Para tls este, para ssl 456 y sin seguridad el 25
$mail->Port = 587;
//definimos usuario y contraseña
$mail->Username = 'tucorreo@tudominio.com';
$mail->Password = 'tu contraseña';
//ahora definimos remitente y si hace falta, réplica
$mail->setFrom('miwebe@midominio.com', 'Lista');
$mail->addReplyTo('miwebe@midominio.com', 'Lista');

$mail->Subject = "Ejemplo de lista con phpmailer";

//Vamos ahora a crear un objeto de conexión a la base de datos
//Tras eso, vamos a recuperar los datos que necesitamos:
$mysqli = new mysqli('server', 'username', 'password', 'db');
$mysqli->set_charset("utf8")
$result = $mysqli->query("SELECT nombre, email FROM mailinglist");

//ahora en bluce vamos recorriendo los resultados y enviando correos

while ($row = $result->fetch_array()) {
    $mail->AltBody = 'Para ver el menasje, please use un  visor de email compatible con HTML';
    $mail->msgHTML($body);
    $mail->addAddress($row['email'], $row['nombre']);
    
    if (!$mail->send()) {
        echo "Mailer Error:" . $mail->ErrorInfo . "<br />";
        break; //forzamos la salida del bucle en caso de error
    } 
    // Limpiamos los datos par próximos envíos
    $mail->clearAddresses();
    $mail->clearAttachments();
}

Como podéis ver no es demasiado complejo gestionar una lista de correo desde PHP y MySQL.

Enviar un email con PHPMailer usando una cuenta de Gmail

Ya hace un tiempo hablamos aquí de cómo enviar correos desde un script de php usando PHPMailer. He decidido hacer una serie de artículos ampliando un poco más el tema.

Una cuestión que puede ser interesante, sobre todo para aquellos que a lo mejor están en un hosting gratuito y no tienen cuentas de correo vinculadas a su dominio, es utilizar una cuenta de Gmail para enviar los correos. Ya que Gmail nos permiter usarlo como cualquier otro correo SMTP esto es factible.

Configurar PHPMailer para usar Gmail es simple, y podéis ver cómo se haría en el siguiente ejemplo (va comentado línea a línea):

/*Lo primero es añadir al script la clase phpmailer desde la ubicación en que esté*/
require '../class.phpmailer.php';

//Crear una instancia de PHPMailer
$mail = new PHPMailer();
//Definir que vamos a usar SMTP
$mail->IsSMTP();
//Esto es para activar el modo depuración. En entorno de pruebas lo mejor es 2, en producción siempre 0
// 0 = off (producción)
// 1 = client messages
// 2 = client and server messages
$mail->SMTPDebug  = 0;
//Ahora definimos gmail como servidor que aloja nuestro SMTP
$mail->Host       = 'smtp.gmail.com';
//El puerto será el 587 ya que usamos encriptación TLS
$mail->Port       = 587;
//Definmos la seguridad como TLS
$mail->SMTPSecure = 'tls';
//Tenemos que usar gmail autenticados, así que esto a TRUE
$mail->SMTPAuth   = true;
//Definimos la cuenta que vamos a usar. Dirección completa de la misma
$mail->Username   = "tucuenta@gmail.com";
//Introducimos nuestra contraseña de gmail
$mail->Password   = "tucontraseña";
//Definimos el remitente (dirección y, opcionalmente, nombre)
$mail->SetFrom('tucuenta@gmail.com', 'Mi nombre');
//Esta línea es por si queréis enviar copia a alguien (dirección y, opcionalmente, nombre)
$mail->AddReplyTo('replyto@correoquesea.com','El de la réplica');
//Y, ahora sí, definimos el destinatario (dirección y, opcionalmente, nombre)
$mail->AddAddress('destinatario@sucorreo.com', 'El Destinatario');
//Definimos el tema del email
$mail->Subject = 'Esto es un correo de prueba';
//Para enviar un correo formateado en HTML lo cargamos con la siguiente función. Si no, puedes meterle directamente una cadena de texto.
$mail->MsgHTML(file_get_contents('correomaquetado.html'), dirname(ruta_al_archivo));
//Y por si nos bloquean el contenido HTML (algunos correos lo hacen por seguridad) una versión alternativa en texto plano (también será válida para lectores de pantalla)
$mail->AltBody = 'This is a plain-text message body';
//Enviamos el correo
if(!$mail->Send()) {
  echo "Error: " . $mail->ErrorInfo;
} else {
  echo "Enviado!";
}

Esencialmente es casi lo mismo que viene en los DOCS que acompañan a los archivos, pero traducido.

Espero que os sirva. Próximamente más PHPMailer, y más programación web.

Automatizar la ejecución de scripts PHP en servidores Linux con cron y cURL

En el pasado ya hablamos en este blog de automatizar tareas en MySQL y de hacer uso de cURL para procesar formularios y enviarlos a un servicio web REST. ¿Y si os digo que cURL también se puede usar para atomatizar la ejecución de scripts PHP?

Seguro que más de una vez has pensado «Molaba que mi página enviara un correo a todos aquellos usuarios que están de cumpleaños» o cosas así. Puedes pensar que da mucho la vara, pero para nada. No sé si lo comenté la otra vez, pero cURL además de poder ser usado desde PHP también puede ser llamado desde la línea de comandos, así que nos permite, desde una consola, llamar a un script de php por protocolo http (bueno, de hecho cURL puede usar varios protocolos como FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE o LDAP) para que se ejecute. Ahora a esto súmale que en un servidor Linux tenemos cron, que nos permite automatizar tareas, como la ejecución de archivos. Solución: programamos que cron ejecute la la llamada a nuestro script de PHP mediante cURL todos los días a una hora concreta.

Veamos un ejemplo en el que ejecutamos un script llamado cumple.php (imagináos que sea un script que felicita el cumpleaños a los usuarios de nuestra web, nos da igual el contenido del script, aquí la clave es automatizar la llamada) todos los días un minuto después de medianoche. Basta con editar el fichero de cron (por ejemplo con crontab -e) y añadir esta línea:

1 0 * * * /usr/bin/curl http://alojamiento/miweb/cumple.php

Bueno, el primer parámetro es el minuto, el segundo la hora y luego irían día del mes, mes y día de la semana (que en este caso van con * porque queremos que sea todos los días, no un día en concreto). Tras los parámetros temporales metemos la orden a ejecutar. En este caso llamamos a cURL (que suele estar en la carpeta /usr/bin/) seguido de la dirección donde esté el script para que sea llamado por protocolo http.

Para mis info sobre cURL puedes buscar aquí, y sobre cron mismamente te ayuda la Wikipedia.

Detectar Internet Explorer con PHP

Es habitual que algunas versiones de Internet Explorer den la tabarra al ser un navegador que no sigue los estándares. Tranquilos, don’t worry. Podéis detectar con PHP si el navegador es Explorer y, dependiendo del resultado, mostrar una maquetación u otra. La función es simple:

<?php
$browser = getenv("HTTP_USER_AGENT");
if (preg_match("/MSIE/i", "$browser"))
{
    //Lo que toque hacer con el Explorer
}
else
{
   //El resto
}
?>

Claro que hay que reconocer que IE9 o IE10 no son tan problemáticos como las versiones anteriores ¿Y si sólo quieres descartar del 8 para abajo? Bueno, toca cambiar un poco el código, para así poder detectar el número de versión (que por suerte está casi extinto, pero todavía arreglo equipos con Internet Explorer 6):

if(preg_match('/(?i)MSIE [5-8]/',$_SERVER['HTTP_USER_AGENT']))
{
   //Aquí lo que va a hacer si es menor que 8
}
else
{
    //Aquí el resto de navegadores y explorers más nuevos.
}

En fin, si queréis sí permitir el 8 y no el 6 y el 7… bueno, pues cambiáis el rango en la expresión regular del ejemplo, y listo.

Deshabilitar cambio de Theme en WordPress

A veces, cuando montas un Wodpress para un cliente, no te interesa que este pueda cambiar el theme. No es por limitar la libertad del cliente, es por el riesgo de que trasteando lo cambie, luego no sea capaz de volver a ponerlo y tengas que ir a hacer mantenimiento (que siempre le puedes cobrar, pero si te está pagando una tarifa fijo por mantenimiento, que es lo normal,  eso acaba en «más trabajo por el mismo precio«). Tranquilo, basta con añadir unas pocas líneas a functions.php y evitaremos que esto ocurra, bloqueando la posibilidad de cambiar el theme del usuario.

add_action('admin_init', 'que_cambie_el_theme_tu_abuela');
function que_cambie_el_theme_tu_abuela() {
  global $submenu, $userdata;
  get_currentuserinfo();
  if ($userdata->ID != 1) {
    unset($submenu['themes.php'][5]);
    unset($submenu['themes.php'][15]);
  }
}

Y point ball (oseáse, punto pelota). Bueno, mejor no le llaméis «que_cambie_el_theme_tu_abuela» a la función, por cosas de profesionalidad, ponedle algo como «bloquear_cambio_theme«, «bloquear_theme«, etc.

Deshabilitar trackbacks en WordPress hacia tus artículos cuando los autorreferencias

Como sabéis todos los que tenéis un blog, los trackbacks son un sistema que os avisa cuando un bloguero ha hecho una referencia a uno de vuestros artículos en el suyo. Pero claro, si tú haces una referencia a uno de tus propios artículos en otro (tipo «Como ya comenté en el artículo X…») el trackback también se envía. Como generalmente no queremos comentarios de nuestros propios trackbacks en nuestros artículos os voy a enseñar un pequeño fragmento de código que, añadido a functions.php deshabilitará el auto-trackback:

function deshabilitar_autoping(&$links){
    foreach ($links as $l=>$link){
      if(0===strpos($link, get_option('home')))
         unset($links[$l]);
    }
}
add_action('pre_ping'; 'deshabilitar_autoping');

Una simple función que os ahorrará tener que estar borrando cada dos por tres vuestros propios trackbacks.

Hashes de passwords en PHP 5.5

Uno de los problemas de los algoritmos criptográficos, como le explicaba el otro día a mi madre, es que al ser susceptibles de poder ser rotos por fuerza bruta cada X tiempo tienen que renovarse, dado que la mayor capacidad de procesamiento de los equipos (y más con la posibilidad de hacer computación distribuida) los hace vulnerables con el paso de los años. Decía un profesor mío, y un gran profesor por cierto, «por fuerza bruta todo se rompe, sólo hace falta tiempo«.

La hasta ahora última versión de PHP nos ofrece nuevas funciones a la hora de trabajar con hashes de passwords. Antes era tradicional utilizar funciones como md5(), sha1(), crypt() o hash(). El problema de usar md5() o sha1() es que usan algoritmos muy directos y ligeros. Es decir, que hacen la encriptación y resumen muy rápido, lo cual aunque beneficia el rendimiento en caso de un ataque (lo más habitual con md5() es el llamado «ataque de cumpleaños» que en lugar de intentar encontrar tu contraseña intenta encontrar simplemente una que de el mismo resultado como resumen) facilita la labor del atacante. En cambio hash() y crypt() son algoritmos más complejos, más lentos, tardan más en devolver el resultado. Algo que para el usuario es un imperceptible segundo de espera, pero que en caso de un ataque que tiene que probar millones de combinaciones se convierte en un poderoso impedimento.

En todo caso PHP 5.5 nos provee de nuevas funciones para trabajar con hashes de passwords:

  • password_get_info
  • password_hash
  • password_needs_rehash
  • password_verify

Las más importantes, en el uso normal que podemos hacer de estas funciones son la segunda y la cuarta. Empecemos entonces por password_hash():

Como su nombre indica, la función genera un hash de tu contraseña. Lógicamente tiene que recibir dicha contraseña como primer parámetro. Como segundo debe recibir una constante con el algoritmo a usar (las opciones son PASSWORD_DEFAULT, que es el que usa por defecto PHP, y PASSWORD_BCRYPT, que nos permite definir varias opciones), y si eliges PASSWORD_BCRYPT puedes pasarle como tercer parámetro una serie de opciones. Las más habituales son cost, que es el «coste de computación» o «número de vueltas» que dará el algoritmo y cuanto más alto sea este número menos rendimiento y más seguridad, y salt, que nos permite «salar» el password (añadirle una cadena para hacer más complejo el asalto a la contraseñas). Un ejemplo con las dos opciones, sacado de la web de php.net


<?php
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";

$options = [
    'cost' => 7,
    'salt' => 'BCryptRequires22Chrcts',
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>

Como podéis ver se hacen dos hashes. El primero con los parámetros por defecto y el segundo usando PASSWORD_BCRYPT con un menor coste para el sistema (el cost en el algoritmo por defecto sería 10) pero con un salt definido. Por lo que he leído el algoritmo por defecto siempre mete un salt aunque no lo definas e incluso se recomienda que mejor dejar a PHP hacerlo antes que meter una cadena estática.

En el caso de password_verify() la cosa es más facililla. La función recibe la contraseña en texto plano y el hash, encriptado con la función password_hash(). Si conciden nos devuelve TRUE y si no coinciden nos devuelve FALSE.

De nuevo me remito al ejemplo del php.net para esto:


<?php
// Ver el ejemplo de password_hash() arriba para ver de dónde viene este hash.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo '¡La contraseña es válida!';
} else {
    echo 'La contraseña no es válida.';
}
?>

Con estas dos funciones podéis hacer de manera muy simple un sistema de login en PHP, con apoyo de una base de datos.

Bluefish, la alternativa libre a Dreamweaver en Linux

Cierto es que yo utilizo Aptana Studio, Eclipse y Geany para todo, por lo que nunca me he preocupado mucho de buscar una alternativa en Linux a Dreamweaver, puesto que ese software apenas lo usaba cuando desarrollaba en Windows por parecerme especialmente incómodo para editar código.

Pero si lo que quieres es un editor que te permita generar código a base de rellenar datos en un formulario y pulsar botones para generar conexiones a bases de datos, tablas o formularios entonces tu alternativa es Bluefish, bastante más sólido y completo que Kompozer, el cual siempre me ha parecido muy limitado.

Logotipo Bluefish Editor 2.2

Bluefish te permitirá trabajar con multitud de lenguajes (podéis ver el listado de lenguajes y CMS soportados en la web del proyecto), dándote referencias en línea, marcadores varios para distintas etiquetas y palabras claves, autocompletado, autocerrado de etiquetas, resaltado de comienzos y finales de bloque, cliente de FTP para trabajar con archivos remotos,  generador de thumbnails…

A pesar del título no sólo está disponible para Linux, sino que hay versiones para Mac, Solaris, BSD y Windows. En fin, si echas en falta el programa de Adobe esta puede ser tu solución.

Se trata de software libre, así que lo podéis descargar gratis desde la propia web o, en caso de Ubuntu, instalarlo con un apt-get install bluefish.

Obtener la url de una página en PHP

Cuando trabajamos con CMS o con frameworks suele ser habitual tener una función que nos indique la url de la página con la que estás trabajando. Pero claro, no siempre trabajamos con frameworks o CMS, a veces nos vemos metidos en el hard-code puro y duro.

En ese caso ¿cómo conseguimos la url? En fin, tendremos que usar la variable $_SERVER.

La variable $_SERVER es un array que almacena cierta información sobre el servidor. En la variable $_SERVER[‘HTTP_HOST’] se guarda la información sobre el nombre de domino, En $_SERVER[‘SERVER_PORT’] el puerto contra el que realizamos la conexión y en $_SERVER[‘REQUEST_URI’] la URI.

Con lo cual si queremos obtener la url basta con usar dos o tres de estas variables, dependiendo de si queremos o no el puerto. Abajo un ejemplo:

//Con puerto
$url_port="http://".$_SERVER['HTTP_HOST'].":".$_SERVER['SERVER_PORT'].$_SERVER['REQUEST_URI'];
//sin puerto
$url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];

Y si vamos a usarlo a menudo lo mejor es utilizarlo dentro de una función que nos devuelva el resultado, para no andar repitiendo código.

Usar codificación utf-8 con MySQLi

Como estos días estoy hablando de MySQLi voy a seguir un poco más con el tema y comentar una peculiaridad: si quieres recuperar datos de una BD donde se hayan guardado en utf-8 y manipularlos e imprimirlos sin problemas de codificación tienes que especificarlo.

Es decir, no basta con que los datos estén en la base codificados como utf-8 y que, además, en la web también esté definido que usaremos esta codificación. No, además debemos indicar a MySQLi que queremos trabajar con utf-8 específicamente. Esto no es para nada complicado porque ya hay una función que te permite elegir la codificación. Un ejemplillo de código simplón os lo aclarará:

$con = new mysqli("server", "user", "password");
if (mysqli_connect_errno($con)) { //comprobamos que no haya fallo en la conexión
	die("Fallo al conenctar a MySQL: " . mysqli_connect_error());
}
				
if (!$con->set_charset("utf8")) {//asignamos la codificación comprobando que no falle
       die("Error cargando el conjunto de caracteres utf8");
}

Como podéis ver, es simple: creamos la conexión y luego simplemente definimos un juego de caracteres. En el ejemplo he usado utf-8, pero vamos, que podéis usar cualquiera de los soportados por MySQL.