<pedrocorreia.net ⁄>
corner
<mySearch ⁄> <mySearch ⁄>

corner
 
corner
<mySnippets order="rand" ⁄> <mySnippets order="rand" ⁄>

corner
 
corner
<myContacts ⁄> <myContacts ⁄>

<email ⁄>


pc@pedrocorreia.net

<windows live messenger ⁄>


pedrojacorreia@hotmail.com

<myCurriculum type="pdf" ⁄>


Download
corner
 
corner
<myBlog show="last" ⁄> <myBlog show="last" ⁄>

corner
 
corner
<myNews show="rand" ⁄> <myNews show="rand" ⁄>

corner
 
corner
<myNews type="cat" ⁄> <myNews type="cat" ⁄>

corner
 
corner
<myQuote order="random" ⁄> <myQuote order="random" ⁄>

corner
 
corner
<myPhoto order="random" ⁄> <myPhoto order="random" ⁄>

<pedrocorreia.net ⁄>
corner
 
corner
<myAdSense ⁄> <myAdSense ⁄>

corner
 
corner
<myVisitorsMap ⁄> <myVisitorsMap ⁄>

corner
 
 

<Construir um simples Captcha ⁄ >




clicks: 9717 9717 2008-05-03 2008-05-03 goto mySnippets mySnippets php  Download  Bookmark This Bookmark This



Cada vez mais se recorre ao Captcha (Completely Automated Public Turing test to tell Computers and Humans Apart), para evitar o spam (que virou recentemente a casa dos 30:)), geralmente encontra-se nas páginas de registo, por ex. quando criamos uma conta Hotmail, Gmail. Até mesmo os fóruns já estão preparados para isso, ou trazem já de raíz ou então são disponibilizados addons. Poderão ver mais detalhadamente o que é isto do Captcha nesta página.

Existem variadissimos tipos de Captcha, o mais comum são as imagens, aquelas coisas esquisitas com traços e letras um bocado para o estranho, inclusivé já vi um que até apareciam uns gatos nas letras, enfim, é até onde a imaginação levar, isto tudo é para que a validação não seja facilmente ultrapassavel pelos leitores OCR.

Pessoalmente gosto do reCaptcha, por vezes aquilo é de tal maneira indecifrável que tem de se fazer refresh :D




Mas o Captcha que vou tentar exemplificar neste snippet não será à base de imagens, em vez de ser mostrada uma imagem ao utilizador, será feita uma pergunta, exactamente, texto (+/-, já explico este +/- lá mais para a frente) perfeitamente normal, neste caso será uma operação matemática.


- style.css, nothing special here, de salientar apenas que recorro ao design elástico, isto é, dou as dimensões em "ems", basicamente faz com que o site se vá adaptando consoante o "zoom" que o utilizador vai fazendo no browser;

  1. body{
  2. font-family: Verdana, Tahoma, Arial, sans-serif;font-size: 62.5%;
  3. color: #000;background: #fff;text-align: left;
  4. }
  5.  
  6. form#my_form_captcha{
  7. padding: 1.5em;
  8. border: solid 0.6em #36393D;
  9. width: 33em;
  10. }
  11.  
  12. form#my_form_captcha span.error{
  13. color: #FF1A00;font-size: 1.2em;
  14. text-decoration: underline;font-weight: 900;
  15. }
  16.  
  17. label{font-size: 1.1em;}
  18.  
  19. label#lbl_my_captcha{
  20. font-weight: 700;font-size: 1.4em;color: #4096EE;
  21. }
  22.  
  23. input{
  24. border: solid .04em #C3D9FF;color: #B02B2C;
  25. background-color: #FFFF88;
  26. }
  27. input#my_captcha_value{
  28. font-size: 2.5em; border-color: #FF7400;text-align: center;
  29. }
  30.  
  31. input#bt_action{
  32. background-color: #356AA0;font-size: 1.4em;color: #F9F7ED;
  33. font-weight: 900;border: 0;
  34.  
  35. }
  36.  
  37. fieldset#my_captcha_fieldset{
  38. text-align: center;width: 31em;border: solid .5em #F9F7ED;
  39. }
  40.  
  41. fieldset#my_captcha_fieldset legend{
  42. border: solid .01em #D15600;font-size: 1.5em;
  43. font-variant: small-caps;padding: .4em;
  44. font-weight: 800;background-color: #FF7400;color: #eee;
  45. }





- AutoLoad.inc, para evitar andar a fazer sempre includes (neste caso não seriam muitos, mas num projecto grande seria mais demorado), vamos implementar uma magic function do php - __autoload, assim que fôr necessário usar uma class, automaticamente esta função é chamada e irá fazer o include a esse ficheiro;

  1. <?php
  2. //class para automaticamente fazer o load das classes
  3. if(function_exists("__autoload")) return;{
  4. function __autoload($class){
  5. include_once("class.$class.php");
  6. }
  7. }
  8. ?>




- class.SingletonBasicMath.php, esta class foi retirada deste snippet, lá encontra-se uma explicação mais detalhada;

  1. <?php
  2.  
  3. /**
  4. * Class SingletonBasicMath
  5. * Esta class serve como exemplo para a implementação
  6. * de PHP Chaining e o uso de Singleton
  7. */
  8. class SingletonBasicMath {
  9.  
  10. private static $_instance;
  11. private $_value = 0;
  12.  
  13. /**
  14. * Método Construtor
  15. * Vai impossibilitar que sejam criadas várias instâncias do objecto
  16. */
  17. public function __construct() {
  18. //permite só que seja criada uma única instância de um objecto (Singleton)
  19. if (!self::$_instance) {self::$_instance = $this;}
  20. }
  21.  
  22. public function Sum($value) {
  23. $this->_value += $value;
  24. return $this;
  25. }
  26.  
  27. public function Subtract($value) {
  28. $this->_value -= $value;
  29. return $this;
  30. }
  31.  
  32. public function Multiply($value) {
  33. $this->_value *= $value;
  34. return $this;
  35. }
  36.  
  37. public function Divide($value) {
  38. $this->_value /= $value;
  39. return $this;
  40. }
  41.  
  42. public function Reset() {
  43. $this->_value = 0;
  44. return $this;
  45. }
  46.  
  47. public function Result() {return $this->_value;}
  48.  
  49. public function toString() {return "<br>Result = {$this->Result()}<br>";}
  50.  
  51. /**
  52. * Eliminar referências do objecto
  53. */
  54. public function __destruct() {
  55. $this->_value = null;
  56. self::$_instance = null;
  57. }
  58. }
  59.  
  60. ?>
  61.  




- class.MyCaptcha.php, é uma class abstracta, ou seja não pode ser instanciada, só pode ser extendida, possui já um conjunto de métodos base que permite construir um Captcha, este é um sistema simples;

  1. <?php
  2.  
  3. /**
  4. * Abstract Class MyCaptcha
  5. *
  6. * Esta class não pode ser instanciada, só extendida.
  7. *
  8. * É implementada a Pattern Singleton
  9. *
  10. * @author Pedro Correia - pedrocorreia.net
  11. */
  12. abstract class MyCaptcha{
  13. private static $_instance;
  14. private $_result;
  15.  
  16. /**
  17. * Método construtor
  18. *
  19. * @param Int Resultado
  20. */
  21. public function __construct($result){
  22. //singleton design pattern
  23. if (!self::$_instance) {self::$_instance = $this;}
  24. $this->_result=$result;
  25. }
  26.  
  27. /**
  28. * Obter Resultado
  29. *
  30. * @return Int
  31. */
  32. public function GetResult(){return $this->_result;}
  33.  
  34. /**
  35. * Verificar se o captcha é ou não válido
  36. *
  37. * @param Int
  38. * @return Boolean
  39. */
  40. public final function Validate($value){return ($value==$this->GetResult());}
  41.  
  42. /**
  43. * Codificar String
  44. *
  45. * @param String Texto a codificar
  46. * @param Bool Codificar tudo?
  47. * @return String
  48. */
  49. public function Encode($string,$encodeAll=true){
  50. $chars = array();
  51. $ent = null;
  52.  
  53. $chars = preg_split("//", $string, -1, PREG_SPLIT_NO_EMPTY);
  54. $count=count($chars);
  55. for ($i = 0; $i < $count; $i++){
  56. if (preg_match("/^(\w| )$/",$chars[$i]) && $encodeAll == false)
  57. $ent[$i] = $chars[$i];
  58. else
  59. $ent[$i] = "&#" . ord($chars[$i]) . ";";
  60. }
  61. if ( sizeof($ent) < 1) return "";
  62.  
  63. return implode("",$ent);
  64. }
  65. }
  66.  
  67. ?>




- class.MySimpleMathCaptcha.php, vamos então construir o nosso Captcha, fazendo o extends à nossa abstract class MyCaptcha;

  1. <?php
  2.  
  3. /**
  4. * Class MySimpleMathCaptch
  5. *
  6. * Esta class vai extender a MyCaptcha e construir
  7. * um Captcha à base de 2 operações numéricas: soma e multiplicação
  8. *
  9. * @author Pedro Correia - pedrocorreia.net
  10. */
  11. class MySimpleMathCaptcha extends MyCaptcha {
  12.  
  13. private $_lnumber=0;
  14. private $_rnumber=0;
  15. private $_operand="+";
  16. private $_allowed_operands="+*";
  17.  
  18. private $_min_rand_number=1;
  19. private $_max_rand_number=32768;
  20.  
  21. //variável estática que identifica o nome a utilizar
  22. //quando o objecto for serializado e guardado na Session
  23. public static $_session_serialize="my_simplemath_captcha";
  24.  
  25. /**
  26. * Método construtor.
  27. *
  28. * @param Int 1º Número
  29. * @param Int 2º Número
  30. * @param Char Operando
  31. * @param Int Número minimo para a aleatoriedade
  32. * @param Int Número máximo para a aleatoriedade
  33. */
  34. public function __construct($lnumber="",$rnumber="",$operand="+",$min_rand="",$max_rand=""){
  35. $is_allowed=strpos($this->_allowed_operands,$operand);
  36. if (!is_numeric($is_allowed)) throw new Exception("OperandIsNotAllowed");
  37.  
  38. if($min_rand) $this->RandNumberMin($min_rand);
  39. if($max_rand) $this->RandNumberMax($max_rand);
  40.  
  41. if(!$lnumber) $lnumber=mt_rand($this->_min_rand_number,$this->_max_rand_number);
  42. if(!$rnumber) $rnumber=mt_rand($this->_min_rand_number,$this->_max_rand_number);
  43. $this->_operand=$operand;
  44. $this->_lnumber=$lnumber;
  45. $this->_rnumber=$rnumber;
  46.  
  47. //criar objecto SingletonBasicMath
  48. $math=new SingletonBasicMath();
  49.  
  50. //verificar qual a operação e obter o seu resultado
  51. switch ($this->_operand){
  52. case "+":
  53. $result=$math->Sum($lnumber)->Sum($rnumber)->Result();
  54. break;
  55. case "*":
  56. $result=$math->Sum(1)->Multiply($lnumber)->Multiply($rnumber)->Result();
  57. break;
  58. }
  59.  
  60. //guardar o resultado
  61. parent::__construct($result);
  62. }
  63.  
  64. /**
  65. * Getter/Setter número minimo para calcular a aleatoriedade
  66. *
  67. * @param Int
  68. * @return Int
  69. */
  70. public function RandNumberMin($value){
  71. if($value) $this->_min_rand_number=$value;
  72. else return $this->_min_rand_number;
  73. }
  74.  
  75. /**
  76. * Getter/Setter número máximo para calcular a aleatoriedade
  77. *
  78. * @param Int
  79. * @return Int
  80. */
  81. public function RandNumberMax($value){
  82. if($value) $this->_max_rand_number=$value;
  83. else return $this->_max_rand_number;
  84. }
  85.  
  86. /**
  87. * Formatar String para ser apresentada ao utilizador
  88. *
  89. * @param String
  90. * @return String
  91. */
  92. public function ToString($encode=true){
  93. $str=sprintf("%d %s %d = ",$this->_lnumber,$this->_operand,$this->_rnumber);
  94. if($encode) $str=$this->Encode($str);
  95.  
  96. return $str;
  97. }
  98.  
  99. }
  100. ?>




Aquando do funcionamento do Captcha, o utilizador irá ver uma conta simples, texto perfeitamente normal, do tipo "X + Y = ", por ex. "18842 + 5879 = ", porém se fizermos o view-source iremos encontrar o seguinte:

&#49;&#56;&#56;&#52;&#50;&#32;&#43;&#32;&#53;&#56;&#55;&#57;&#32;&#61;&#32;


Mas continuando ...


Os controlos Captcha geralmente estão associados a um formulário, por ex. quando criamos uma conta, registamos uma newsletter, sendo o HTTP protocolo stateless (não guarda estados entre postbacks), como então iremos ultrapassar isso? Ou seja neste snippet a pergunta será uma operação aritmética simples, mas o resultado dessa operação é calculada no lado do servidor, ou seja, quando é construido o formulário não é enviado no html qualquer tipo de informação que indique qual seja o resultado do Captcha (por ex. hidden fields ou cookies).


Vamos então recorrer ao serialize e unserialize para que possamos guardar o objecto na Session, ou seja, vamos serializar o objecto, e guardá-lo na Session, depois que o formulário seja submetido vamos "buscar" o objecto à Session e desserializá-lo (será que esta palavra existe no Português de Camões :D), isto faz com que tenhamos o nosso objecto como o tinhamos inicialmente, isto é, podemos aceder normalmente ao objecto como senão tivessemos feito um postback. Para as operações de *serialize* recorri ao base64_encode e base64_decode, isto faz com que por ex. poupemos espaço na Session e não tenhamos problemas com o encoding do objecto, por ex. as aspas (") ou plicas (')








- ProcessRequests.php, temos este ficheiro auxiliar, que vai controlar os pedidos do formulário. Por ex. se o formulário ainda não tiver sido submetido vai construir o formulário; caso o formulário tenha sido submetido vai verificar se o Captcha está correctamente preenchido, se estiver tudo apresenta uma mensagem de sucesso, caso contrário exibe novamente o formulário, porém com uma mensagem de aviso, de que o Captcha não foi correctamente preenchido. É aqui que vamos fazer o unserialize ao objecto;

  1. <?php
  2. session_start();
  3.  
  4. $action=$_POST["bt_action"];
  5.  
  6. //não há postback, estamos a aceder pela 1ª vez à página
  7. if(!$action) {
  8. $output=BuildForm();
  9. }
  10.  
  11. //há um postback, vamos verificar se o captcha está ou não correcto
  12. //se estiver correcto enviamos para o output uma mensagem de sucesso,
  13. //caso contrário, construímos novamente o formulário
  14. else if($action){
  15.  
  16. //buscar o objecto à Session
  17. $session_name=MySimpleMathCaptcha::$_session_serialize;
  18. $session=$_SESSION[$session_name];
  19.  
  20. //se o objecto se encontra na session, vamos então fazer o unserialize
  21. if($session){
  22. //fazer o unserialize ao objecto para que podemos aceder aos
  23. //seus métodos e atributos
  24. $captcha=unserialize(base64_decode($session));
  25.  
  26. //verificar se o resultado inserido pelo utilizador é o resultado do captcha
  27. //para tal recorremos ao método "Validate"
  28. if($captcha->Validate($_POST["my_captcha_value"])){
  29. $output="Obrigado pelo registo ...";
  30. }
  31. else{ //o captcha está errado
  32. $output=BuildForm("Verifique se inseriu correctamente o resultado");
  33. }
  34. }
  35. //por algum motivo o objecto não se encontra na Session,
  36. //vamos construir novamente o formulário
  37. else{
  38. $output=BuildForm();
  39. }
  40. }
  41.  
  42.  
  43. /**
  44. * Construir Formulário, CAPTCHA e guardar o objecto serializado na sessão
  45. *
  46. * @return String
  47. */
  48. function BuildForm($error_msg=""){
  49.  
  50. //construir CAPTCHA
  51. $captcha=new MySimpleMathCaptcha();
  52.  
  53. //guardar objecto serializado na sessão
  54. $_SESSION[MySimpleMathCaptcha::$_session_serialize]=base64_encode(serialize($captcha));
  55.  
  56. if($error_msg) $error_msg="<span class='error'>$error_msg</span><br/><br/>";
  57.  
  58. return "
  59. <form method='post' action='' id='my_form_captcha'>
  60. $error_msg
  61. <label for='my_email' id='lbl_my_email'>E-mail</label>
  62. <input type='text' id='my_email' name='my_email' size='20' />
  63. <br/><br/>
  64.  
  65. <fieldset id='my_captcha_fieldset'>
  66. <legend>Insira o Resultado</legend>
  67. <label for='my_captcha_value' id='lbl_my_captcha'>{$captcha->ToString()}</label>
  68. <input type='text' value='' name='my_captcha_value' id='my_captcha_value' size='10' />
  69. </fieldset>
  70.  
  71. <br/><br/>
  72. <input type='submit' value='Enviar Dados' id='bt_action' name='bt_action' />
  73.  
  74. </form>
  75. ";
  76. }
  77.  
  78.  
  79. ?>




- por fim o ficheiro que vai juntar as peças todas

  1. <?php
  2. include_once ("Classes/AutoLoad.inc");
  3. include_once ("ProcessRequests.php");
  4. ?>
  5.  
  6. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
  7. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  8. <head>
  9. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  10. <title>MyCaptcha</title>
  11. <link rel="stylesheet" type="text/css" href="css/style.css" media="all" />
  12. </head>
  13. <body>
  14. <?php
  15. echo $output;
  16. ?>
  17. </body>
  18. </html>





Este será o output esperado:




Caso o utilizador erro no resultado (como por ex. no screenshot de cima, o resultado está errado), vai ser presenteado com o seguinte:



Além de aparecer a mensagem de informação errada, é gerado outro Captcha.



Qualquer erro/ dúvida é só dizer!









clicks: 9717 9717 2008-05-03 2008-05-03 goto mySnippets mySnippets php  Download  Bookmark This Bookmark This