Validar E-mails con PHP

Muchas veces en nuestros desarrollos nos toca validar campos y uno de los más importantes es el campo de “Email”.

La típica forma “expresiones”

La típica forma de realizar esto es buscando caracteres inválidos dentro de la cadena…
Hace poco tiempo, salió en la red el sitio emailvalido.com, dentro del cual colocabas tu e-mail y te decía si era valido o no.

Me llamó mucho la atención, ¿Cómo hace esto?, estuve investigando y me acordé que los servidores smtp tienen dos comandos “vrfy” y “expn” pero por seguridad estos se encuentran deshabilitados, ¿entonces cómo verificar un mail?

mediante RCPT TO, cuando se introduce un email valido que existe en el sistema este responde con un código numérico (250).

Por eso me he montado una clase, que espero que les pueda ser útil a la hora de checar mails

/**
 * Clase para validar mails (http://www.coders.me - Coders community)
 *
 * Esta clase corre unicamente sobre Linux y  PHP5
 * (siempre  y cuando esté activado el modulo de sockets en PHP)
 * para más información relativa a este código visite: http://www.coders.me
 * http://www.rfc-es.org/rfc/rfc1869-es.txt
 *

 * @author 	Amir Canto Palomo <amircanto@hotmail.com>
 * @copyright  validEmail Class  2008-02-23
 * @version 1.3
 * @todo Hacer que la clase pueda contactar con servidores SMTP que soporten SSL
 * @license MIT

  The MIT License
  Copyright (c) 2008 www.coders.me 

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
 */

define("CRLF","\r\n");		// [ENTER]
define("PORT","25");  		// SMTP PORT.

Class ValidEmail
{
	private $mail;
	private $user;
	private $domain;

	public function validate()
	{
		if($sock = $this->connectSMTP())
		{
			if($this->getResponse($sock) == "220")
			{
				$this->writeData($sock,"EHLO ".$this->domain.CRLF);
				// echo "Mandando helo $this->domain\n";
				if($this->getResponse($sock) == "250")
				{
					$this->writeData($sock,"HELO ".$this->domain.CRLF);
					if($this->getResponse($sock) == "250")
					{
						$this->writeData($sock,"MAIL FROM: $this->user@".$this->domain.CRLF);
						if($this->getResponse($sock) == "250")
						{

							$this->writeData($sock,"RCPT TO: ".$this->user."@".$this->domain.CRLF);
							if($this->getResponse($sock) == "250")
							{
								// echo "email valido\n";
								$this->writeData($sock,"QUIT".CRLF);
								$this->socketClose($sock);
								return 1; // valid email
							}
						}
					}
				}
			}
		}
		return 0;
	}

	private function socketClose($socket)
	{
		socket_close($socket);
	}

	private function writeData($socket,$data)
	{
		if($socket)
		{
			if(socket_write($socket,$data,strlen($data)))
			{
				return 1;
			} 

		}
		return 0;
	}
	private function getResponse($socket)
	{
		if($socket)
		{
			// echo "Esperando respuesta\n";
			$response = socket_read($socket,2048);
			// echo "respuesta: $response\n";
			if(strlen($response) > 0 )
			{

				//echo $response;
				$rescode = $response[0].$response[1].$response[2];

				return $rescode;
			}
		}
	}
	private function connectSMTP()
	{

		if( function_exists("socket_create") && function_exists('socket_connect') )  //Ok.. existen las funciones..
		{

			if( empty($this->domain) || ($this->domain == "") ) $this->extractData();
			if($sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP))
			{

				$mxrecords = $this->getMxRecords();
				/*echo count($mxrecords)."\n";
				echo "records:".(int)$mxrecords."\n";
				print_r($mxrecords);*/
				if(isset($mxrecords) && (int)$mxrecords > 0 )
				{
					if($this->validString() )
					{
						foreach($mxrecords as $records)
						{
							if(isset($records) && $records != "")
							{
								// echo "Conectando con: $records \n";
								$address = gethostbyname($records);
								if($address != $records)
								{

									if(socket_connect($sock,$address,PORT))
									{
										// echo "conectado";
										return  $sock; //Conected :)  devolvemos el handle
									}
								}
							}

							}
					} else return 0;
				} else return 0;
			}

		}
		return 0; // no logramos conectar / something has failed.. we cannot connect.
	}
	public function validString()
	{

		$email = $this->mail;
		if( eregi( "^([a-z0-9._]+)@([a-z0-9.-_]+).([a-z]{2,4})$", $email) )
		{
			return 1;
		}
		return 0;
	}

	public function getMxRecords()
	{
		$dominio = $this->domain;
		if( isset($dominio) && ($dominio != "") )
		{
			if(getmxrr($dominio,$records))
			{
				if(count($records) > 0)	// hay mx records...
				{
					return $records;
				}
				else 	// NO hay MX records.. entonces usamos el dominio para conectar.
				{
					return $dominio; // retornamos el dominio tal cual..
				}
			} else
			{
				return 0; // algo fallo... no pudimos conectar
			}
		}
		return 0; // de nueva cuenta algo falló...
	}
	private function extractData()
	{
		$data = explode("@",strtolower($this->mail));
		$this->user = $data[0];
		$this->domain = $data[1];
	}

	function __construct($email)
	{
		$this->mail = $email;
		$this->extractData();
	}
}

Pequeña explicación de la clase

Lo que hace esta clase es al crear el objeto y pasarle el mail para despues llamar a las funciones correspondientes es:

  • Validar la cadena y verificar si tiene caracteres invalidos
  • Buscar el servidor smtp usando los registros mx obtenidos mediante el dominio del mail.
  • Conectar al servidor smtp si no puede intenta con el siguiente registor MX.
  • Una vez conectado comienza a interactuar con el servidor y le manda el mail usando RCPT TO: y espera la respuesta con el código 250 (que significa que el servidor validó correctamente el e-mail)

Uso de la clase

$validar = new ValidEmail("correo@micorreo.com");
if($validar->validString())	 // el email tiene caracteres validos
{
	echo "el email tiene caracteres validos";
	if($validar->validate())	// Ahora comprobamos que exista la cuenta en el servidor.
	{
		echo "el dominio es valido";
	}
}

Las dos funciones aquí importantes son “validString()” y “validate()”

validString()

Si queremos validar solamente los caracteres del mail podemos usar unicamente esta funcion, si la cadena esta limpia devuelve true y si tiene caracteres invalidos devuelve false.

validate()

La función validate() será la forma de validar mediante el servidor smtp, devuelve true si el mail es invalido, false en caso contrario.

¿Por qué no usar la función validString() dentro de validate()?

Por que no todos vamos a querer validar mails de las dos formas de una sola vez, habrá quien solo quiera verificar los caracteres.

Errores

Hasta ahora solo he testeado la clase 2 o 3 veces. Por lo que si alguien encuentra algún error sería bueno que nos lo comente.

Errores conocidos

Por el momento la clase solo puede validar mails en los cuales los servidores smtp no estén configurados para usar SSL.

Descargar

Si quieres descargar el código fuente y los archivos de ejemplo puedes hacerlo aqui.

Actualizaciones:

Bugs fixeados:

  • MX Records, cuando el dominio es invalido y no hay records devuelve 0 y el parámetro es pasado al foreach y por lo tanto falla
Jaben says:

waa. wena. saludos camaradas ;) , buena empresa llevan.

salu!

Feran says:

Excelente, me sera de mucha utilidad, son unos genios.

caos30 says:

La idea de que algo así pueda funcionar me parece un logro admirable, pero me haríais un favor si me explicarais un ejemplo concreto de para qué se podría utilizar ¿?

Al principio he pensado que podría servir para comprobar si es correcta la dirección de email introducida por un visitante. Pero la verdad es que “comprobar SI EXISTE la cuenta de email no demuestra que esté bien escrita”, pues uno podría equivocarse al escribir una cuenta de hotmail y fácilmente la cuenta escrita existiría… habida cuenta de que deben haber millones casi iguales!!! Así que tal método, no ofrecería una fiabilidad completa ni siquiera si la clase que has programado funcionara perfectamente bien con cualquier dominio. ¿No creeis?

Sin embargo, tal como he empezado diciendo, agradecería comentarios al respecto, puesto que puede ser que no esté viendo al respuesta obvia a mi misma pregunta ;)

Un saludo!
Viva el software libre.
SERGI

Amir says:

@caos30

El punto de ‘para que demonios sirve esta idea’ es muy clara..
tiene muchos usos, si te pones a ver el más común es en un formulario de registro, pero en el ámbito de negocios si tú tienes un sistema web para el envío de email marketing (como promoemail.com.mx un ejemplo)
Te sería DE GRAN UTILIDAD utilizar la clase para extraer de tu DB los emails que sean falsos, ya no existan o comprobar que no tengan caracteres raros.

De hecho existe un programa que realiza tal acción, esta idea se me ocurrió (de hacer esta clase) por que en donde yo trabajaba (una división de promoemail.com.mx) necesitaban saber la validez de los emails. (o al menos eso escuché una vez)

Tiene muchos fines, muchos usos, depende de tí encontrarlos.
Si la clase no está terminada es por que no soy un adicto a liberar código y si lo libero lo hago de manera incompleta, no me gusta regalar mi trabajo < - esto es un comentario de mi filosofía personal, puedo hacer que la clase funcione con servidores SMTP, pero y que gano yo con regalarsela? por que no mejor alguien busca la solución, recodea la clase y listo!

Hay un aporte más… yo solo mostré la puerta, depende de alguno de nosotros cruzarla (y mejorarla ;) )
no quiero ofender a nadie, pero no soy repito un asiduo del software libre ni nada.

De hecho jaja cuando trabajaba para una empresa de telecomunicaciones como programador PHP, en mi PC coloqué un wallpaper que decía “Comunistas!” y un tipo detrás de un programador jajaja “te lo dicen tus amigos de microsoft” o algo así. (recuerdo que casi me agarran a patadas por los compañeros que eran pro-software libre)

en fin, hay que respetar pensamientos, ideologias y filosofías.

Un saludo! y espero verte comentando más por aquí =)

Este comentario te ha servido? y colabora con el blog
Reply
Amir says:

PD: si se mal interpreta mi comentario, disculpas por otro lado, creo que no tengo tiempo para estarme ocupando de recodear clases, pero otros sí y podrían mejorarla y aumentarla. (si no creeiss que no tengo tiempo, miren el nivel de posts de eduardo vs los mios jajaja)

un saludo

Este comentario te ha servido? y colabora con el blog
Reply
caos30 says:

Perdona que no te haya entendido bien en cuanto a tu posición respecto al software libre. Me tomé la libertad de gritar un “viva el software libre” en la firma de mi primer comentario, porque entendí que tú estabas a favor del mismo, pues tu código lo distribuyes según la licencia MIT (eso es lo que pones en el artículo) y esta licencia genera software libre !!.

No soy abogado, ni la Wikipedia es el sitio perfecto para dilucidar estas cuestiones, pero aquí encontrarás un breve resúmen de las características y connotaciones de la licencia MIT:

http://es.wikipedia.org/wiki/Licencia_MIT

Seguiré los próximos posts de vuestro blog por RSS. De momento lo encuentro interesante. Tal como tú dices: cualquier “parte de código” puede ser útil aunque no esté acabado ;)

Un saludo!
SERGI

Amir says:

@SERGI: Sí lo libero bajo la licencia MIT :) que no sea “pro-software libre” no quiere decir que no utilice las licencias ,(aunque por razones personales creo que jamás utilizaría la GPL, la siento muy radical).

Un saludo :) !
PD: espero no haberte ofendido.

Este comentario te ha servido? y colabora con el blog
Reply
Eduardo says:

Hola caos30,
Yo le obligué aconsejé a Amir poner ese tipo de licencia :) (él en un principio no iba a ponerle nada xD)
Es muy obvio que si el código que ponemos al público es libre, todos tanto ustedes como nosotros, nos beneficiamos.

Fíjate hace poco hice mi sexy-lightbox y 2 días después Nahuel me enviaba el plugin para usarlo en wordpress :P

Me gusta la MIT porque es la que mayor libertad da. La GPL es muy restrictiva!

Saludos!

Este comentario te ha servido? y colabora con el blog
Reply
caos30 says:

Bueno, no vayáis a llevaros una idea equivocada de mí… pues no sé mucho de las licencias. Por más artículos que leo sobre ellas… nunca recuerdo las “sutiles” diferencias que hay entre ellas, jejejeje…

En fin, que no pasa nada chicos… lo mejor siempre es aclarar los malosentendidos.

Y por cierto, excelente la sexy-lightbox. A mi mujer le encantó y ya andamos pensando como implementarla ;)

Gracias por compartir!
Salud!
SERGI

valugi says:

esta class valida tambien correos tipo hotmail or gmail?

Amir says:

Sí así es :) valida correos de hotmail, de gmail no ya que sus servidores requieren autenticación… y la clase no tiene soporte para SSL…

Tal vez en una próxima versión y sí… o alguien se anime a implementarle el SSL… que sería genial, yo por cuestiones de tiempo no lo puedo hacer (al menos no en este momento).

Un saludo!

Fabian says:

Excelente su pagina enserio desde que la conoci me meto para leer sus articulos ya que me parecen muy interesantes y ps es entretendio leer y documentarse con su forma de redactar. Buen trabajo.

wowweed.es says:

dios ke wen tuto, millones de gracias

mail says:

prefiero seguir usando las expresiones regulares :)

alurahosey says:

reliable scaled costs back environmental mitigation increasing estimate order [url=http://www-sul.stanford.edu]gps growing orbital disease[/url] http://www.gather.com

Jose says:

Lo probe y lo primero que note es que intenta validad emails con el dominio, por ejemplo en correo@correo.com intenta validad conectando con el servidor pop correo.com cuando en general el servidor pop3 de un dominio suele ser pop.correo.com, por otro lado si el dominio esta hosteado en google o si el pop utilisa ssl/tsl tampoco funciona por lo que (calculo) mas de un 80% me va dar un resultado erroneo.
Es muy dificil validar si el correo existe en realidad o no, hasta ahora lo que mas me resulta es capturar los emails rebotados y procesarlos.
Saludos

Just desired to post and question where you obtained your layout? I’m shopping around for one for my current blogging site and really appreciate yours. Thanks so much.

With havin so much content do you ever run into any issues of plagorism or copyright violation? My website has a lot of unique content I’ve either written myself or outsourced but it seems a lot of it is popping it up all over the web without my permission. Do you know any ways to help protect against content from being ripped off? I’d truly appreciate it.

Pedro florez says:

Me sale el siguiente error

el email tiene caracteres validos
Fatal error: Call to undefined function getmxrr() in C:\AppServ\www\vmail\class.validmail.php on line 181

Agradezco su ayuda.

Pedro