PHP典型模块与项目实战大全
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 热点关键技术

1.2.1 防SQL注入技术

SQL注入是指一些精通SQL语句的用户,通过在表单或浏览器地址栏中输入SQL语句来绕过系统检验的一种技术。一般防范SQL注入的手段是过滤敏感字符,例如引号等。

PHP相对于ASP来说,要安全得多。但这不代表PHP网站就不会出现SQL注入的情况。幸运的是,在PHP上防SQL注入,要比ASP简单、方便,不需要写一大段的转换语句,只要几个函数就可以了。

1.intval()函数

intval()函数的作用是返回变量的整数值,函数语法如下:

      int intval ( mixed var [, int base] )

2.addslashes()函数

addslashes()函数就是在操作数据库时,对其中的特殊字符进行自动转义,即在特殊字符前加上反斜线(\),包括单引号(' )、双引号(")、NULL,但是不包括“%”和“_”。函数语法如下:

      string addslashes ( string str )

此外,还可以使用mysql_real_escape_string()来进行转义,效果与addslashes()函数一样。

1.2.2 Ajax无刷新验证技术

Ajax(Asynchronous JavaScript and XML)即异步JavaScript和XML,是时下最流行的技术。Ajax不是新的技术,而是原有技术的集合,这从它的名字上就能够看出来。

Ajax的核心技术是XMLHttpRequest。通过XMLHttpRequest中的open()方法和send()方法,可以在不刷新当前页面的情况下向处理页发送数据;通过XMLHttpRequest中的responseText属性和responseXML属性,可以得到处理页的输出结果。

Ajax能够流行的原因,是因为它能为用户提供良好的交互性,这个特性在本模块中被充分地体现出来。注册用户无须经过“漫长”的等待就可以知道输入的账号是否可用,登录用户也不必担心因为输错登录信息而重新刷新整个页面。

使用Ajax,一般分为下面几步。

(1)创建XMLHttpRequest对象。不同的浏览器,创建XMLHttpRequest对象及使用的方法有一些差别,这里只针对IE浏览器进行创建,代码如下:

      var xmlhttp = false;                                    //初始化变量
      //如果ActiveXObject存在,说明是IE 1.0以上的版本,否则使用XMLHttpRequest创建
      if(window.ActiveXObject){
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      }else if(window.XMLHttpReuqest){
            xmlhttp = new XMLHttpRequest();
      }

(2)对象创建成功后,就可以使用对象中的open()方法创建新请求了,方法格式如下:

      xmlhttp.open(rmethod, rurl, isAsync);

参数说明如下。

❑ rmethod:指明请求的方法,如get()或post()。

❑ rurl:指明请求的页面。可以是绝对地址,也可以是相对地址。

❑ isAsync:指明请求是否为异步。默认为True,即异步。

(3)如果isAsync等于True,那么当请求的状态改变时,将调用onreadystatechange属性,该属性指定了一个回调函数,格式如下:

      xmlhttp.onreadystatechange = reabackfunc;

或者

      xmlhttp.onreadystatechange = function(){…}

(4)在回调函数中,首先需要判断http的请求状态和http状态码。这是通过readyState属性和status属性来判断的。readyState属性有5种状态值,常用的是4,表示数据接收完毕。status属性的值比较多,常用的是200,表示请求成功。一般使用这两个属性一起来判断,格式如下:

      xmlhttp.onreadystatechange = function(){
            if(readyState == 4 and status == 200){
              …
            }
      }

(5)当响应页处理结束后,即满足了“readystate==4 and status == 200”这个条件,就可以使用XMLHttpRequest对象的属性获取响应页的值了。常用的有responseText、responesXML、responseStream等,这里以responseText为例进行介绍。

responesText属性是将响应页的输出信息作为字符串返回,格式如下:

      str = xmlhttp.responseText;

(6)最后使用send()方法来接收回应。send()方法可以传递数据,但这取决于open()方法中的rmethod参数,当参数为get时,数据是附在URL中进行传递的;当参数为post时,数据只能使用send()方法进行传递。send()方法的语法如下:

      xmlhttp.send([rdate]);

1.2.3 验证码技术

验证码技术是为了防止用户名和密码被暴力破解,在登录页面上生成一组随机数,每次刷新页面时,随机数都会改变。一般是4位,也有更多位的。

1.获取验证码

PHP中的验证码可以通过rand()函数生成随机数的方式得到。rand()函数可以获取指定范围内的随机数,该函数语法如下:

      int rand([int min, int max])

如果省略两个参数,那么将返回0到RAND_MAX之间的随机整数。否则,返回min和max之间的整数。例如本例中要获取4位十六进制的整数,代码如下:

      <? php
            for($i=0; $i<4; $i++){
              $num .= dechex(rand(0,15));               //生成随机数
            }
            …
      ?>

函数dechex()可以将参数转换为十六进制数表示。

使用JavaScript也可以生成十六进制随机数,但是稍有些复杂。JavaScript中不能直接将十进制数转换为十六进制数,需要手动进行转换。首先使用Math.random()函数生成0~15的随机数,然后使用Math.ceil()函数将随机数取整,接下来就要逐次判断该值,如果该值大于9,那么将10~15的数一一对应转换为a、b、…、f。转换完成后,将值累加,最后传给valcode.php页。

使用JavaScript生成十六进制随机数的完整代码如下:

      //生成随机数
      function showval(){
            num = ' ' ;
            for(i=0; i<4; i++){                           //循环输出4位验证码
              tmp = Math.ceil((Math.random() * 15)); //取得1位十六进制的整数
                    if(tmp > 9){                          //依次判断随机数
                        switch(tmp){
                          case(10):                       //如果随机数等于10,则转换为a
                              num += ' a' ;
                              break;
                          case(11):
                              num += ' b' ;               //如果随机数等于11,则转换为b
                              break;
                          case(12):
                              num += ' c' ;               //如果随机数等于12,则转换为c
                              break;
                          case(13):                       //如果随机数等于13,则转换为d
                              num += ' d' ;
                              break;
                          case(14):                       //如果随机数等于14,则转换为e
                              num += ' e' ;
                              break;
                          case(15):                       //如果随机数等于15,则转换为f
                              num += ' f' ;
                              break;
                        }
                    }else{
                        num += tmp;
                    }
            }
            $(' chkid' ).src=' valcode.php? num=' +num;   //将生成的随机数传给图像生成页
            $(' chknm' ).value = num;                     //将随机数的值保存到页面的隐藏域中
      }

2.显示随机数图片

显示随机数的方法很多,将随机数写入一个图片中再显示是目前常用的方法。在PHP中,可以使用GD函数库来实现。使用到的函数主要有imagecreate()函数、imagecolorallocate()函数、imagestring()函数、imagesetpixel()函数、imagepng()函数和imagedestroy()函数。

1)imagecreate()函数

imagecreate()函数用来创建一个基于调色板的空白图像源,这是生成图片的第一步,函数语法如下:

      resource imagecreate ( int width, int height )

其中,参数width和height分别指定了图像的宽和高。

2)imagecolorallocate()函数

imagecolorallocate()函数可以为创建后的图像分配颜色,函数语法如下:

      int imagecolorallocate ( resource image, int red, int green, int blue )

参数说明如下。

❑ image:一个图像源。

❑ red、green和blue:表示红、黄、蓝三元素的成分,每种颜色的取值范围在1~255。

3)imagestring()函数

图像创建完成后,就可以使用imagestring()函数来添加图像文字了,该函数的语法如下:

      bool imagestring ( resource image, int font, int x, int y, string s, int col )

参数说明如下。

❑ image:一个图像源。

❑ font:可以设置字体,如果使用系统默认字体,则可以使用1~5的数字。

❑ x和y:分别表示文字相对于整幅图像的x轴和y轴坐标,即所输入的字符串的左上角坐标。

❑ s:要显示的字符串。

❑ col:为字体颜色,也是使用imagecolorallocate()函数来分配的。

4)imagesetpixel()函数

使用imagecolorallocate()创建的是一个单一背景色的图像,如果希望向图像中添加干扰码,则可以使用imagesetpixel()函数,该函数的作用是画一个像素点,函数语法如下:

      bool imagesetpixel ( resource image, int x, int y, int color )

参数说明与imagestring()函数相似,这里不再赘述。

5)imagepng()函数

该函数将创建完成的图片以png的格式输出,函数语法如下:

      bool imagepng ( resource image [, string filename] )

参数说明如下。

❑ image:要保存的图像源。

❑ filename:要保存的图像名。如果省略,则直接输出到浏览器。

6)imagedestroy()函数

图像保存完毕后,使用imagedestroy()函数来释放内存,函数语法如下:

      bool imagedestroy ( resource image )

验证码的生成文件是valcode.php,其完整代码如下:

      <? php
       header("content-type:image/png");           //设置页面编码
       $num = $_GET[' num' ];                      //获取超链接传递的随机数
       $imagewidth=60;                             //定义画布的宽
       $imageheight=18;                            //定义画布的高
       $numimage = imagecreate($imagewidth, $imageheight);         //创建画布
       imagecolorallocate($numimage,240,240,240);                  //设置画布颜色
       for($i=0; $i<strlen($num); $i++){                           //循环读取随机数
            $x = mt_rand(1,8)+$imagewidth*$i/4;
            $y = mt_rand(1, $imageheight/4);
       $color=imagecolorallocate($numimage, mt_rand(0,150), mt_rand(0,150), mt_rand
       (0,150));                                                   //定义图像的颜色
            imagestring($numimage,5, $x, $y, $num[$i], $color);   //将随机数写入画布中
       }
        for($i=0; $i<200; $i++){                //for循环语句生成干扰线
        $randcolor=imagecolorallocate($numimage, rand(200,255), rand(200,255), rand
        (200,255));                             //定义颜色
                //生成干扰线
                imagesetpixel($numimage, rand()%70, rand()%20, $randcolor);
        }
        imagepng($numimage);                    //生成图像
        imagedestroy($numimage);                //释放资源
       ?>

1.2.4 E-mail激活技术

为了防止恶意注册,现在有很多的网站采用了E-mail激活技术。当用户注册成功后,并不是马上就可以使用,而是需要先登录邮箱。通过系统发出的E-mail上的链接进行激活,只有激活后,账号才可以使用。对于找回密码的用户,系统会将新密码发送到邮箱中,这在一定程度上,提高了安全性。

本模块使用ZendFramework中的Zend_Mail组件完成邮件的发送操作,在本书的光盘中没有提供Zend_Mail组件,如果要运行这个程序,需要读者自己下载Zend组件,并将其复制到本模块的根目录下,即mr/04文件夹下。

实践真知 说明

在执行邮件的发送时,笔者使用的是搜狐邮箱,邮箱名称是mrsoft8888@sohu.com,密码是mrsoft8888。读者在完成用户注册之后,可以登录这个邮箱完成注册用户的激活操作,否则注册的用户是不能够使用的。

注册用户激活的原理是控制数据库中用户信息表(tb_member)中active字段的值,默认值注册成功后该字段的值是0,但是此时该用户是不能够登录网站的,必须通过邮箱激活,将该字段的值更新为1之后,此用户才可以使用。激活操作的文件存储于activation.php文件中,其关键代码如下:

      <? php
      session_start();
      header(' Content-Type:text/html; charset=gb2312' );
      include_once("conn/conn.php");
      if (! empty($_GET[' name' ]) && ! is_null($_GET[' name' ])){         //激活注册用户
            $num=$conne->getRowsNum("select * from tb_member where name
            =' ".$_GET[' name' ]."' and password = ' ".$_ GET[' pwd' ]."' ");
            if ($num>0){
              $upnum=$conne->uidRst("update tb_member set active = 1 where name=
              '".$_GET[' name' ]."' and password = ' ".$_GET[' pwd' ]."' ");
              if($upnum > 0){
                  $_SESSION[' name' ] = $_GET[' name' ];
                  echo "<script>alert(’用户激活成功!' ); window.location.
                  href=' main.php' ; </script>";
              }else{
                  echo "<script>alert(’您已经激活!' ); window.location.href=' main.php' ;
                    </script>";
              }
            }else{
              echo "<script>alert(’用户激活失败!' ); window.location.href=
              'register.php' ; </script>";
            }
      }
      ?>

1.2.5 应用键盘响应事件验证信息是否合法

新用户注册时,随着信息输入,系统实时显示信息的正确性;用户登录时,无须使用鼠标来选择文本框,只要按<Enter>键,焦点就自动下移。这些功能都是通过JavaScript脚本中的键盘响应事件来实现的。随着Ajax技术的流行,JavaScript已经成为一个开发人员必须要掌握的技术。下面,就来简单了解键盘事件。

用户通过onkeydown和onkeyup事件来触发响应事件。使用方法与onclick事件类似。onkeydown表示当键盘上的键被按下时触发,onkeyup和它正好相反,当键盘上的键被按下又抬起时触发。在页面中加载事件的方式有多种,这里介绍两种最常用的方式。

1.将事件直接添加到页面元素中

方法最直接、简单,格式如下:

      <script type="text/javascript">
          …
          function refer(){
            …
          }
      </script>
      <input type="text" onkeydown="refer()" />

当该用户输入完信息后,单击任意键,onkeydown事件被触发,并调用refer()函数。

2.通过window.onload加载

当页面被载入时,事件被载入,格式如下:

      <script>
            window.onload = function(){
              document.getElementById(' lgname' ).onkeydown = function(){
                    …
              }`
            }
      </script>
      …
      <input id="lgname" type="text" />
      …

当用户输入信息时,每输入一个字母,都将触发该事件,在该事件调用的函数中,对用户输入信息进行判断。例如用户名必须大于等于2、密码最短6位、E-mail必须合法等。下面看一段验证用户名的代码:

      //验证用户名
      //为id等于regname的页面元素添加onkeyup事件
      //用户每按一次键,都会调用一次该函数
      $(' regname' ).onkeyup = function (){
            name = $(' regname' ).value;                //获取输入内容
          cname2 = ' ' ;
          if(name.match(/^[a-zA-Z_]*/) == ' ' ){        //判断输入字符是否在有效范围之内
              $(' namediv' ).innerHTML = ' <font color=red>必须以字母或下画线开头</font>' ;
              cname1 = ' ' ;
          }else if(name.length < 2){                 //判断输入的字符长度
              $(' namediv' ).innerHTML = ' <font color=red>注册名称必须大于等于2位</font>' ;
              cname1 = ' ' ;
          }else{
              $(' namediv' ).innerHTML = ' <font color=green>注册名称符合标准</font>' ;
              cname1 = ' yes' ;
          }
          chkreg();
      }
      …

该段代码的运行结果如图1.6所示。

图1.6 验证信息合法性

使用onkeydown事件还可以实现对特定键的控制,包括<Enter>键、<Ctrl>键、<Alt>键等所有的按键,这是通过在onkeydown事件中使用keyCode属性来实现的。keyCode属性能够知道用户按下的是哪个键,例如<Enter>键等于13、空格键等于32等。使用keyCode属性的一般格式如下:

      <script>
          window.onload = function(){
              document.getElementById(' lgname' ) = function(){
                  if(event.keyCode == 13){            //判断用户是否按了<Enter>键
                      …
                  }
              }`
          }
      </script>

在本模块中,实现了焦点自动下移功能。当用户按<Enter>键时,页面的焦点自动移到下一个文本框中,实现后的效果如图1.7所示。

图1.7 焦点自动下移的运行效果

1.2.6 PHP中操作Cookie技术

Cookie的作用是当用户第一次访问某服务器时,服务器将一些信息保存到客户端计算机内。在以后的一段时间内,当用户再次访问这个服务器时,服务器通过Cookie信息,能够识别该用户。在很长一段时间内,Cookie都被当做一个不安全的因素,很少再被使用,转而使用Session。但是,Cookie的功能确实十分方便,自动登录、网站统计等都比其他的实现方式要简单、快捷得多。Cookie只是一个文本文件,不能访问本地硬盘,无法传播病毒木马程序。唯一要注意的地方是,Cookie只能识别计算机,而不会在意谁在使用。

在PHP中操作Cookie使用setcookie()函数和$_COOKIE预定义变量。setcookie()函数的语法格式如下:

      bool setcookie ( string name [, string value [, int expire [, string path [, string
  domain [, bool secure]]]]] )

参数说明如下。

❑ name:设置了Cookie的名字。

❑ value:Cookie的值,即参数name的值。

❑ expire:用来设置Cookie的过期时间,该参数以时间戳的形式存在。一般设置Cookie过期时间的时候,通过time()+秒数来实现,如time()+60×10,表示Cookie将在10分钟后失效。设置Cookie马上失效,可以将时间设为当前日期之前,如time()-1,那么Cookie会立即失效。

❑ path:表示Cookie在服务器端的有效范围。如果path设为“/”,那么Cookie在整个服务器内都有效,如果Cookie为“/05/”,那么Cookie只在服务器下的05目录有效。

❑ domain:指定了Cookie有效的域名范围,以www.mrbccd.com为例,如果domain设为“.mrbccd.com”,那么Cookie在该域的所有子域都有效;如果domain为“www.mrbccd.com”,那么Cookie只在www.mrbccd.com域内有效。

❑ secure:表明是否在https传送。如果是True, Cookie仅在https连接中被设置,默认是False。

1.2.7 在JavaScript中操作Cookie技术

在JavaScript中可以通过document对象中的Cookie属性对Cookie进行创建、读取、删除等操作。先来看创建Cookie的格式(代码中加粗的部分是需要用户设置的)。

      documen.cookie = "Name=Value; expires=time; path=URL; domain= Domain"

其中各个参数的含义与PHP中的基本相同,这里不再赘述。

在JavaScript中读取Cookie有一点复杂。在JavaScript看来,Cookie就是一个字符串。例如一个名字为“Count”、值是“10”的Cookie,在JavaScript中的格式是

      count=10

如果有多个Cookie,那么它的格式如下:

      count=10; name=mr; …

而真正需要的值,其实只有10而已。所以,要获取Cookie的值,就要使用split()函数将Cookie进行拆分,获取count值的代码为

      <script>
                 if(document.cookie==' ' ){                      //Cookie等于空时
                    document.write(' no cookie' );
                 }else{
                    //以分号为分隔符,对Cookie进行分隔
                    cookiearr = document.cookie.split(' ; ' );
                     leng = cookiearr.length;                   //获取数组长度
                     for(i=0; i<leng; i++){
                          //判断哪一个Cookie等于count
                          if(cookiearr[i].split(' =' )[0] == ' count' ){
                               //输出count的值
                               document.write(cookiearr[i].split(' =' )[1]);
                         }
                    }
         }
</script>

1.2.8 用户自动登录技术

自动登录的原理是:当用户打开登录页面时,登录页面首先判断用户客户端机器中的Cookie值。如果该值不存在,或者Cookie已失效,这时,将显示登录表单;如果Cookie值存在,则直接使用Cookie中所保存的用户名进行登录。

在本模块中,用户首先打开的是index.php页,该页判断Cookie值,如果没有,则跳转到login.php进行登录;如果Cookie有值,则将Cookie中的用户名保存到Session,直接进入主页面,实现代码如下:

      <? php
            session_start();                                //开启Session支持
            header(' Content-Type:text/html; charset=gb2312' );      //设置页面编码
            //判断Cookie是否为空
            if(! empty($_COOKIE[' name' ]) and ! is_null($_COOKIE[' name' ])){
              $_SESSION[' name' ] = $_COOKIE[' name' ];     //将Cookie保存到Session中
                //跳转到main.php页
              header(' location:http://localhost/model/05/01/main.php' );
            }else{                                          //Cookie为空,说明没有登录
                //跳转到login.php页
              header(' location:http://localhost/model/05/01/login.php' );
            }
      ?>

这里$_COOKIE[' name' ]的值是在登录成功时设置的,相关代码如下:

      <? php
            …                                                //如果登录成功
            setcookie(' name' , $name, time()+60*10);         //保存$name的同时设置时间
            …
      ?>