PHP 实现用户注册登录功能

前端页面制作

一、介绍

1.1 基础内容

本次实验主要以前端制作为主要内容,使用纯前端知识制作用户注册登录的界面,为后续实验做铺垫。通过本实验的学习,我们可以轻松地制作类似的界面效果。由于这是 PHP 的项目课,所以我们并不需要一行一行的写 html 代码,本次课程的界面制作主要使用前端框架 bootstrap3 的模板和组件来快速搭建大方漂亮的界面,大家也会领略到 bootstrap 框架的方便之处。

如果你对页面制作不感兴趣或者对这方面比较熟悉,可以跳过本次实验,直接下载使用实验文档最后提供的代码文件。

1.2 知识点

  • Bootstrap3页面布局
  • Jquery脚本编写
  • Ajax异步通信

1.3 实验环境

  • 编程环境:Ubuntu1404
  • MySQL数据库
  • PHP内置服务器
  • sublime,一个方便快速的文本编辑器。点击桌面左下角: 应用程序菜单/开发/sublime。

1.4 最终效果图

æ­¤å¤è¾å¥å¾ççæè¿°

 æ­¤å¤è¾å¥å¾ççæè¿°

1.5 代码获取

你可以通过下面命令将代码下载到环境中,作为参照对比进行学习

wget http://labfile.oss.aliyuncs.com/courses/587/index.php
wget http://labfile.oss.aliyuncs.com/courses/587/check.js

二、实验步骤

2.1 页面制作

2.1.1 主页

首先在 /home/Code 目录下建立一个 web 文件夹作为我们的网站根目录,在该目录下建立 index.php 作为网站首页。使用 sublime 打开web 文件夹,对 index.php 编辑:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Index</title>
    <link rel="stylesheet" href="">
</head>
<body>
    <h1>hello shiyanlou</h1>
</body>
</html>

接下来启动服务器,自 PHP5.4之后 PHP 内置了一个 Web 服务器,因此我们可以很方便地运行服务器,在 web 目录下执行以下命令:

$ php -S localhost:80   #若启动失败,可将端口号换为 8080

不出意外地话,我们的服务器就启动了,打开浏览器,输入 URL :localhost:80

æ­¤å¤è¾å¥å¾ççæè¿°

扫描二维码关注公众号,回复: 8665422 查看本文章

好了,接下来我们将对这个页面进行精心的制作!

2.1.2 模板挑选

由于我们的课程是 PHP 的项目课,所以我们的重点不是去设计精美的界面,但是也不能太随意应付,毕竟写出来也要给别人欣赏。所以我们可以到网上去寻找好看免费的模板,自己再进行加工,就是自己的作品了。本课程主要使用 bootstrap 框架制作界面,所以首先应该去 bootstrap 官网去看看相关的优秀作品。大家平时开发也可以到上面去寻找一丝创作灵感。

å®ä¾

经过一番寻找,我选择了这个模板:

模æ¿

大家也可以选择自己喜欢的模板,制作出自己喜欢的界面。我将会对这个模板页面进行一下改造,首先,先教大家如何使用这个模板。

  • 复制源代码:查看该模板的源代码,全部复制并粘贴到我们自己 index.php 中(覆盖之前的测试代码);

  • 使用 css:在 web 目录下建立一个 public 文件夹,并在其下建立 img,css,js文件夹,分别用来放置我们对应的文件。

    我们可以看到模板的源代码的header中有这样一段注释:<!-- Custom styles for this template --> 下面就是该模板所用的自定义的 css 文件。在网页中单击 css 链接,复制 css 代码,存放到 public/css/start.css 文件(自行建立文件)。

  • 调整引用位置:在 index.php 使用相对路径调整 css 文件的引用路径。

  • 标签页图标:自行找一个 ico 文件放到 public/img 下,并修改 <link rel="icon" href="public/img/xxxx.ico">,调整引用目录

2.1.3 目录结构及修改后代码

目录结构:

ç®å½

Index.php 代码:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="public/img/favicon.ico">

    <title>Index</title>

    <!-- Bootstrap core CSS -->
    <link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="public/css/start.css" rel="stylesheet">
    <link href="public/css/footer.css" rel="stylesheet">
  </head>

  <body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <!-- 这里做了修改 -->
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#register">Register</a></li>
            <li><a href="#login">Login</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>
    <!-- 页面主体内容 -->
    <div class="container">
            <div class="content">
              <div class="starter-template">
                <!-- 这里做了修改,其他地方自由发挥 -->
                <h1>Welcome To ShiYanLou</h1>
                <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
              </div>    
            </div>
    </div><!-- /.container -->

    <!-- 网页底部 -->
    <footer class="footer">
        <div class="container">
        <p class="text-muted">
          <h2><a href="http://www.shiyanlou.com" title="www.shiyanlou.com" style="color: blue;">www.shiyanlou.com</a></h2>
        </p>
      </div>
    </footer>

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
    <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  </body>
</html>

这里我还添加了一个网页的 footer,对应的 footer.css 文件代码:

/* Sticky footer styles
-------------------------------------------------- */
.footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  /* Set the fixed height of the footer here */
  height: 60px;
  text-align:center;
  background-color: #f5f5f5;
}

对应的 start.css 的代码:

body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}

现在再次打开 localhost:80 ,应该就可以看到一个漂亮的首页了,而且还是响应式的布局。这里展示我的首页!

æ­¤å¤è¾å¥å¾ççæè¿°

2.1.4 注册表单制作

我们将把注册和登陆的表单全部放到 index.php 中。为了美观,我们把表单样式做成模糊框样式的效果。

这里我们要使用到 bootstrap 的模糊框的 js 插件 modals 。

把模糊框的代码片段放到 container 的 div 中去。将它作为注册的表单。同时修改导航栏的 register 按钮的相关参数和模糊框的id,使之与模糊框对应起来。

<li><a href="#Register" data-toggle="modal" data-target="#register">Register</a></li>

注册表单代码:

  <!-- 页面主体内容 -->
    <div class="container">
      <div class="content">
          <div class="starter-template">
                <!-- 这里做了修改,其他地方自由发挥 -->
            <h1>Welcome To ShiYanLou</h1>
            <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
          </div>  
          <!-- 注册表单 -->
          <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" id="register" aria-labelledby="myLargeModalLabel">
            <div class="modal-dialog modal-lg">
              <div class="modal-content">
              <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="myModalLabel">Register</h4>
              </div>
              <form action="" method="post" accept-charset="utf-8" class="form-horizontal">
                <div class="modal-body">

                  <div class="form-group">
                    <label for="username" class="col-sm-4 control-label">Name:</label>
                    <div class="col-sm-6">
                      <input type="text" class="form-control" name="username" id="username" minlength="2" maxlength="20" placeholder="username" required="">
                    </div>
                    <!-- 错误提示信息 -->
                    <h6 style="color: red;" id="dis_un"></h6>
                  </div>

                  <div class="form-group">
                    <label for="email" class="col-sm-4 control-label">Email:</label>
                    <div class="col-sm-6">
                      <input type="email" class="form-control" name="email" id="remail" placeholder="Email" required="">
                    </div>
                    <!-- 错误提示信息 -->
                    <h6 style="color: red;" id="dis_em"></h6>
                  </div>

                  <div class="form-group">
                    <label for="password" class="col-sm-4 control-label">Password:</label>
                    <div class="col-sm-6">
                      <input type="password" class="form-control" name="password" id="password" placeholder="password" minlength="6" maxlength="20" required="">
                    </div>
                    <!-- 错误提示信息 -->
                    <h6 style="color: red;" id="dis_pwd"></h6>
                  </div>

                  <div class="form-group">
                    <label for="confirm" class="col-sm-4 control-label">Confirm password:</label>
                    <div class="col-sm-6">
                      <input type="password" class="form-control" name="confirm" id="confirm" placeholder="confirm" minlength="6" maxlength="20" required="">
                    </div>
                    <!-- 错误提示信息 -->
                    <h6 style="color: red;" id="dis_con_pwd"></h6>
                  </div>

                  <div class="form-group">
                    <label for="code" class="col-sm-4 control-label"> verification code :</label>
                    <div class="col-sm-6">
                      <input type="text" class="form-control" name="code" id="code" placeholder="verification code" required="" maxlength="4" size="100">
                    </div>
                  </div>
                  <div class="form-group">
                    <div class="col-sm-12" style="center">
                      <img src="#" alt="" id="codeimg" >
                      <a href="#" title="Switch">Click to Switch</a>
                    </div>
                  </div>
                </div>

                <div class="modal-footer">
                  <button type="button" class="btn btn-default" data-dismiss="modal" style="float: left;">Close</button>
                  <input type="reset" class="btn btn-warning" value ="reset" />
                  <button type="submit" class="btn btn-primary" id="reg">register</button>
                </div>
              </form>
              </div>
            </div>
          </div>
      </div><!-- /.content -->

    </div><!-- /.container -->

修改完以上代码,保存之后,再次打开主页,点击 register 按钮,就能看到一个好看的模糊框表单:

register

2.1.5 登陆表单制作

参照注册表单相似的做法,我们快速搭建一个登录表单

修改导航栏菜单:

<li><a href="#Login" data-toggle="modal" data-target="#login">Login</a></li>

在注册表单之后添加以下登陆表单代码:

 <!-- 登陆表单 -->
          <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" id="login" aria-labelledby="myLargeModalLabel">
            <div class="modal-dialog modal-lg">
              <div class="modal-content">
              <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title" id="myModalLabel">Login</h4>
              </div>
              <form action="" method="post" accept-charset="utf-8" class="form-horizontal">
                <div class="modal-body">

                  <div class="form-group">
                    <label for="email" class="col-sm-4 control-label">Email:</label>
                    <div class="col-sm-6">
                      <input type="email" class="form-control" name="email" id="email" placeholder="Email" required="">
                    </div>
                  </div>

                  <div class="form-group">
                    <label for="password" class="col-sm-4 control-label">Password:</label>
                    <div class="col-sm-6">
                      <input type="password" class="form-control" name="password" placeholder="password" minlength="6" maxlength="20" required="">
                    </div>
                  </div>
                  <div class="form-group">
                    <label for="code" class="col-sm-4 control-label"> verification code :</label>
                    <div class="col-sm-6">
                      <input type="text" class="form-control" name="code" id="code" placeholder="verification code" required="" maxlength="4">
                    </div>
                  </div>

                  <div class="form-group">
                    <div class="col-sm-12" style="text-align:center">
                      <img src="#" alt="" id="codeimg">
                      <span>Click to Switch</span>
                    </div>
                  </div>

                </div>

                <div class="modal-footer">
                  <button type="button" class="btn btn-default" data-dismiss="modal" style="float: left;">Close</button>
                  <input type="reset" class="btn btn-warning" value ="reset" />
                  <button type="submit" class="btn btn-primary" name="login">Login</button>
                </div>
              </form>
              </div>
            </div>
          </div>

现在打开主页,点击登陆按钮。

login

2.2 Js 脚本编写

2.2.1 分析脚本功能

我们经常会填写各种各样的表单,也经常会有这样的体验:当你输入数据有误或者不符合规范时候,表单页面就会给出各种错误提示,让你去改正。比如让你输入邮箱地址,你却输入了电话号码,这就是不符合规范的。当你填写用户名的时候,如果网站要求用户名具有唯一性,那么就需要对你的用户名进行检测,并给出相关提示,比如 ‘用户名已被注册’等信息。这些功能大部分都是通过前端 js 代码实现的。这样做的原因,一方面是为了让表单输入更人性化,节约输入成本。另一方面也是为了减轻后台服务器的压力。本次实验我们也会带着大家做一些表单的输入检测,如果你细心的话,可能已经注意到了,我在注册表单的输入框之后添加了对应的错误提示代码。由于注册和登陆表单功能类似,为了示范作用,我们这里只做注册表单的 js 代码,登陆部分可以留做作业,大家自己去完善。

我们需要实现的 js 功能:

  • 用户名唯一性检测
  • 邮箱格式和唯一性检测
  • 密码与确认密码输入检测

Javascript库:Jquery

2.2.2 代码编写

为了目录结构的规范性,我们先在 public/js/ 下建立 check.js。并在 index.php 底部添加引入代码:

 <script src="public/js/check.js"></script>

接下来就开始认真的写 js 代码了!

为了体现代码的规范性,我将各个功能封装为函数,最后再统一调用。

$(document).ready(function() {

    var nameFlag = true;    //准备一个用户名的可用性标识
    var emailFlag = true;    //准备一个邮箱的可用性标识
    var pwdFlag = true;   //准备一个密码的可行性标识

  //此处添加检测方法
  function checkName (){}        //检测用户名
  function checkEmail (){}        //邮箱检测
  function checkPassword (){}            //密码检测
  function checkSubmit (){}        //表单提交检测

  checkName ();
  checkEmail ();
  checkPassword ();
  checkSubmit ();
    })

2.2.3 用户名检测

这里我采用实时的方法对用户名的输入做检测:当用户输入一个字符,就做一次检测,判断合法性和唯一性。因为要检测用户名唯一性,就需要从数据库里去查找用户名,这涉及到后台代码的功能,不过现在我们只关心前台的功能,这里使用 Ajax 与后台交互,实现实时检测。代码如下:

    function checkName () {        //检测用户名
        $('#username').keyup(function() {    //用户输入一个字符就触发响应
            var length = $(this).val().length; 
            if ( length >= 2 && length <= 20 ) {    //判断用户名长度 2至20位之间
                //发送用户名,检测的类型为 name
                $.post('##', {username: $(this).val(),type:'name'}, function(data, textStatus, xhr) {
                    if (textStatus == 'success') {
                        if (data == '1') {    //如果后台返回1,则表示此用户名已被注册
                            $('#dis_un').text('UserName is already registered');    //给出错误提示
                            nameFlag = false;
                        }else{    //用户名可以使用
                            $('#dis_un').text('');    //去掉错误提示文字
                            nameFlag = true;
                        }
                    }
                });
            }else{    //长度不符合则不进行检测
                $('#dis_un').text('');
            }
        });
    }

2.2.4 注册邮箱检测

这里的邮箱输入,我们不仅需要对其做唯一性检测,还需要做格式检测,判断用户输入是否是邮箱格式。我们也需要使用 Ajax 与后台交互确认唯一性,不过不需要像用户名那样实时检测,只需要在其失去焦点之后就开始检测,代码如下:

    function checkEmail () {        //邮箱检测
        $('#remail').blur(function() {    //注册邮箱失去焦点才检测
                if ($(this).val() != '') {    //输入不为空就检测
                    var reg = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/;    //正则表达式判断邮箱格式
                    if (reg.test($(this).val())) {    //是邮箱格式
                        $.post('##', {email: $(this).val(),type: 'email'}, function(data, textStatus, xhr) {
                            if (textStatus == 'success') {
                                if (data == '1') {    //后台返回1 表示已被注册
                                    $('#dis_em').text('E-mail is already registered');
                                    emailFlag = false;
                                }else{    //邮箱可用
                                    $('#dis_em').text('');
                                    emailFlag = true;
                                }
                            }
                        });
                    }else{    //不是邮箱格式
                        $('#dis_em').text('E-mail format is incorrect');
                        emailFlag = false;
                    }
                }else{    //为空,不显示提示文字
                    $('#dis_em').text('');
                }
            });
    }

2.2.5 密码与确认密码检测

  • 密码输入的规则:不能为空,长度至少为6位。
  • 确认密码规则:不能为空,长度至少6位。当上面输入密码不为空,确认密码与输入密码须一致。

基于以上规则,实现代码:

    function checkPassword () {            //密码检测
        $('#password').blur(function(){
                if ($(this).val() == '') {
                    $('#dis_pwd').text('Password cannot be empty');
                }else if($(this).val().length < 6){
                    $('#dis_pwd').text('Passwords must be at least six');
                }else{
                    $('#dis_pwd').text('');
                }
            });

            $('#confirm').blur(function() {    //确认密码检测
                var val = $('#password').val();
                if (val != '') {
                    if ($(this).val() == '') {
                        $('#dis_con_pwd').text('Please confirm your password');
                        pwdFlag = false;
                    }else if($(this).val() != val){
                        $('#dis_con_pwd').text('Confirm password inconsistent');
                        pwdFlag = false;
                    }else{
                        $('#dis_con_pwd').text('');
                        pwdFlag = true;
                    }
                }else{
                    $('#dis_con_pwd').text('');
                    pwdFlag = false;
                }
            });
    }    

2.2.6 提交检测

当点击表单注册按钮时,我们需要判断表单界面是否存在错误,若有误,则提示检测页面信息,否则提交。

    function checkSubmit () {
        $('#reg').click(function() {
            if (!(nameFlag && emailFlag && pwdFlag)) {    //用户名标识 && 邮箱标识 && 密码标识
                alert('Please check page info!');
                return false;
            }
        });
    }

至此,注册表单部分的js代码就写完了,可能写的不是很完善,但是希望可以抛砖引玉。大家也可以自行编写相关的 js 检测代码。

2.2.7 运行效果

当注册表单输入有误时,点击了 register 按钮:

check

部分需要与后台交互配合的效果需要在后续的实验中才能实现:用户名与邮箱的唯一性检测。

三、实验总结

本次实验主要以制作注册登陆页面为主,让大家学会如何使用免费的模板,以及相应的表单制作。虽然这是 PHP 的项目课,但是作为一名 PHP 程序员,这些简单的前端技术和简单的页面制作也是必须要掌握的。虽然做出来的页面不是很精美,但是也达到了我们想要的效果。如果有兴趣的同学,可以自行设计精美的网页。

四、课后作业

参考注册表单的js代码,自行设计编写登陆表单的输入检测 js 脚本。

验证码制作

一、实验介绍

1.1 实验内容

本次实验将会带领大家使用面向对象的思想封装一个验证码类。并在注册和登陆界面展示使用。通过本次实验的学习,你将会领悟到 PHP 的 OOP 思想,以及 GD 库的使用,验证码生成。

1.2 涉及到的知识点

  • PHP编程
  • GD库使用
  • OOP编程

1.2 实验环境

  • 实验楼在线编程环境
  • PHP内置服务器
  • sublime,一个方便快速的文本编辑器。点击桌面左下角: 应用程序菜单/开发/sublime

1.3 效果图

éªè¯ç 1

 éªè¯ç 

二、封装验证码类

2.1 建立目录以及准备字体

在 web 目录下建立一个 admin 目录作为我们的后台目录,存放后台代码文件。在 admin 下建立一个 fonts 目录,用于存放制作验证码所需字体。

在 admin 下新建一个 Captcha.php 文件,这就是我们需要编辑的验证码类文件。

当前目录层次结构:

æ­¤å¤è¾å¥å¾ççæè¿°

编辑 Captcha.php 文件:

<?php 
/**
* Captcha class
*/
class Captcha
{

    function __construct()
    {
        # code...
    }
}

添加该类的私有属性和构造方法:

<?php 
/**
* Captcha class
*/
class Captcha
{
    private $codeNum;    //验证码位数
    private $width;    //验证码图片宽度
    private $height;    //验证码图片高度
    private $img;    //图像资源句柄
    private $lineFlag;    //是否生成干扰线条
    private $piexFlag;    //是否生成干扰点
    private $fontSize;    //字体大小
    private $code;    //验证码字符
    private $string;    //生成验证码的字符集
    private $font;    //字体
    function __construct($codeNum = 4,$height = 50,$width = 150,$fontSize = 20,$lineFlag = true,$piexFlag = true)
    {
        $this->string = 'qwertyupmkjnhbgvfcdsxa123456789';    //去除一些相近的字符
        $this->codeNum = $codeNum;
        $this->height = $height;
        $this->width = $width;
        $this->lineFlag = $lineFlag;
        $this->piexFlag = $piexFlag;
        $this->font = dirname(__FILE__).'/fonts/consola.ttf';
        $this->fontSize = $fontSize;
    }
}

字体文件可通过以下命令下载到 fonts 目录:

$ wget http://labfile.oss.aliyuncs.com/courses/587/consola.ttf

接下来开始编写具体的方法:

  • 创建图像资源句柄

    //创建图像资源    
    public function createImage(){
            $this->img = imagecreate($this->width, $this->height);    //创建图像资源
            imagecolorallocate($this->img,mt_rand(0,100),mt_rand(0,100),mt_rand(0,100));    //填充图像背景(使用浅色)
        }
    

    用到的相关函数

  • 创建验证码字符串并输出到图像

    //创建验证码    
    public function createCode(){
            $strlen = strlen($this->string)-1;
            for ($i=0; $i < $this->codeNum; $i++) { 
                $this->code .= $this->string[mt_rand(0,$strlen)];    //从字符集中随机取出四个字符拼接
            }
              $_SESSION['code'] = $this->code;    //加入 session 中
    
          //计算每个字符间距
            $diff = $this->width/$this->codeNum;
            for ($i=0; $i < $this->codeNum; $i++) { 
                        //为每个字符生成颜色(使用深色)
              $txtColor = imagecolorallocate($this->img,mt_rand(100,255),mt_rand(100,255),mt_rand(100,255));
              //写入图像
                imagettftext($this->img, $this->fontSize, mt_rand(-30,30), $diff*$i+mt_rand(3,8), mt_rand(20,$this->height-10), $txtColor, $this->font, $this->code[$i]);
            }
        }
    

    用到的相关函数:

  • 创建干扰线条

    //创建干扰线条(默认四条)
    public function createLines(){
            for ($i=0; $i < 4; $i++) { 
                $color = imagecolorallocate($this->img,mt_rand(0,155),mt_rand(0,155),mt_rand(0,155));    //使用浅色
                imageline($this->img,mt_rand(0,$this->width),mt_rand(0,$this->height),mt_rand(0,$this->width),mt_rand(0,$this->height),$color); 
            }
        }
    

    用到的相关函数:

  • 创建干扰点

    //创建干扰点    (默认一百个点)
    public function createPiex(){
            for ($i=0; $i < 100; $i++) { 
                $color = imagecolorallocate($this->img,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255));
                imagesetpixel($this->img,mt_rand(0,$this->width),mt_rand(0,$this->height),$color);
            }
        }
    

    使用的相关函数:

  • 对外输出图像:

        public function show()
        {
            $this->createImage();
            $this->createCode();
            if ($this->lineFlag) {    //是否创建干扰线条
                $this->createLines();
            }
            if ($this->piexFlag) {    //是否创建干扰点
                $this->createPiex();
            }
            header('Content-type:image/png');    //请求页面的内容是png格式的图像
            imagepng($this->img);    //以png格式输出图像
            imagedestroy($this->img);    //清除图像资源,释放内存
        }
    

    用到的相关函数:

  • 对外提供验证码:

    public function getCode(){
            return $this->code;
        }
    

完整代码如下:

<?php 
/**
* Captcha class
*/
class Captcha
{
    private $codeNum;
    private $width;
    private $height;
    private $img;
    private $lineFlag;
    private $piexFlag;
    private $fontSize;
    private $code;
    private $string;
    private $font;
    function __construct($codeNum = 4,$height = 50,$width = 150,$fontSize = 20,$lineFlag = true,$piexFlag = true)
    {
        $this->string = 'qwertyupmkjnhbgvfcdsxa123456789';
        $this->codeNum = $codeNum;
        $this->height = $height;
        $this->width = $width;
        $this->lineFlag = $lineFlag;
        $this->piexFlag = $piexFlag;
        $this->font = dirname(__FILE__).'/fonts/consola.ttf';
        $this->fontSize = $fontSize;
    }

    public function createImage(){
        $this->img = imagecreate($this->width, $this->height);
        imagecolorallocate($this->img,mt_rand(0,100),mt_rand(0,100),mt_rand(0,100));
    }

    public function createCode(){
        $strlen = strlen($this->string)-1;
        for ($i=0; $i < $this->codeNum; $i++) { 
            $this->code .= $this->string[mt_rand(0,$strlen)];
        }
        $_SESSION['code'] = $this->code;
        $diff = $this->width/$this->codeNum;
        for ($i=0; $i < $this->codeNum; $i++) { 
            $txtColor = imagecolorallocate($this->img,mt_rand(100,255),mt_rand(100,255),mt_rand(100,255));
            imagettftext($this->img, $this->fontSize, mt_rand(-30,30), $diff*$i+mt_rand(3,8), mt_rand(20,$this->height-10), $txtColor, $this->font, $this->code[$i]);
        }
    }

    public function createLines(){
        for ($i=0; $i < 4; $i++) { 
            $color = imagecolorallocate($this->img,mt_rand(0,155),mt_rand(0,155),mt_rand(0,155));
            imageline($this->img,mt_rand(0,$this->width),mt_rand(0,$this->height),mt_rand(0,$this->width),mt_rand(0,$this->height),$color); 
        }
    }

    public function createPiexs(){
        for ($i=0; $i < 100; $i++) { 
            $color = imagecolorallocate($this->img,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255));
            imagesetpixel($this->img,mt_rand(0,$this->width),mt_rand(0,$this->height),$color);
        }
    }

    public function show()
    {
        $this->createImage();
        $this->createCode();
        if ($this->lineFlag) {
            $this->createLines();
        }
        if ($this->piexFlag) {
            $this->createPiexs();
        }
        header('Content-type:image/png');
        imagepng($this->img);
        imagedestroy($this->img);
    }

    public function getCode(){
        return $this->code;
    }
}

以上就是验证码类的全部代码。看起来确实挺简单的,不过用的图像处理函数比较多,上面相关的函数我也做了必要的链接和用途说明。这些函数也不用死记硬背,遇到不清楚的,随时查阅 PHP 官方文档,最重要的是还有中文文档。

2.2 使用验证码

既然已经封装完毕,那就可以开始使用了。这里为了方便,直接在 Captcha 类的下方调用该类:

session_start(); //开启session
$captcha = new Captcha();    //实例化验证码类(可自定义参数)
$captcha->show();    //调用输出

三、前端展示

后端已经准备好了验证码,前端界面就可以展示了,修改 index.php 中的注册与登陆表单的验证码部分:

<div class="form-group">
  <div class="col-sm-12" style="text-align: center">
      <img src="admin/Captcha.php" alt="" id="codeimg" onclick="javascript:this.src = 'admin/Captcha.php?'+Math.random();">
      <span>Click to Switch</span>
  </div>
</div>

img 标签添加了点击事件的 js 代码,这样就可以实现点击更换验证码的功能!

效果图:

ææå¾

四、实验总结

到目前为止,我们的验证码模块基本就完成了。学习到这里,大家应该对面向对象编程有了进一步的理解。也领悟到了一丝 OOP 思想。OOP 的三大特征:封装,继承,多态。我们这里只用到了一点封装的思想。大家可以继续完善和改进这个验证码类,设计出更加完美的类。这个实验我们也体会到了PHP 的函数很多,不要死记硬背,多看官方文档。

https://www.shiyanlou.com/courses/587/labs/1968/document

实现注册登陆功能

一、实验介绍

1.1 实验内容

本次实验将接着上次实验继续进行。利用 OOP 思想封装注册和登陆的后台逻辑,与前台配合实现注册登陆功能。

1.2 实验知识点

  • PHP编程
  • mysqli 扩展对数据库操作
  • session会话

1.3 实验环境

  • sublime,一个方便快速的文本编辑器。点击桌面左下角: 应用程序菜单/开发/sublime。

  • MySQL数据库,使用下面的命令开启服务:

    $ sudo service mysql start
    # 自行建立 web 数据库,users 表:id,username,email,password
    # 可使用以下 SQL 语句建立数据表:
    CREATE TABLE users(
    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(20) NOT NULL,
    email VARCHAR(50) NOT NULL,
    password CHAR(32) NOT NULL
    );
    
  • 开启服务器:

    $ php -S localhost:80 #若启动失败,可将端口号换为 8080
    

二、实现注册功能

2.1 建立文件

在 admin 目录下建立 Register.php 作为后台登陆处理程序,稍后我们将会在其中封装具体的注册逻辑。

同时修改首页中注册表单的提交地址。

<form action="admin/Register.php" method="post" accept-charset="utf-8" class="form-horizontal">

新建配置文件,因为要使用到数据库,需要使用一些连接参数。我们可以单独建立一个配置文件,把这些参数都定义好。

在 web 目录下建立一个 config.php 文件,这就是我们的配置文件了,先定义一些数据库的连接参数:

<?php
session_start();    //开启session,在其他位置引用时可以自动开始
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'web');
define('DB_USER', 'root');
define('DB_PWD', '');

前提是你在数据库里已经建立好了 web 数据库和 users 表。

2.2 逻辑编码

  • 定义类并添加属性和构造方法:

    <?php 
    /**
    * register
    */
    class Register
    {
        private $username;    //用户名
        private $db;    //数据库实例
        private $email;    //邮箱
        private $pwd;    //密码
        private $con;    //确认密码
        private $code;    //验证码
        function __construct()
        {
            if (!isset($_POST['type'])) {    //    若不是通过post方式访问,则返回
                echo "<script>alert('You access the page does not exist!');history.go(-1);</script>";
                exit();
            }
            require '../config.php';    //引入配置文件
            $this->db = new mysqli(DB_HOST,DB_USER,DB_PWD,DB_NAME) or die('数据库连接异常');    //实例化mysqli连接
        }
    }
    
  • 判断用户名唯一性(结合前端 Ajax)

        public function uniqueName()
        {
          //判断是否是通过ajax方式提交数据
            if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) ) {
                if('xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])){
                    $this->username = $_POST['username'];    //接受post的用户名
                    $sql = "SELECT count(*) FROM users WHERE username = '".$this->username."'";        //构造查询语句
                    $count = mysqli_fetch_row($this->db->query($sql))[0];//获取结果集中索引为0的值
                    if ($count) {    //$count不为0,则存在相同的用户名
                        echo "1";
                    }else{
                        echo "0";
                    }
                }else{    //不是ajax方式
                    echo "hello world";
                }
            }else{    //不是ajax方式
                echo "hello world";
            }
        }
    
  • 判断邮箱唯一性(结合前端 Ajax)

        public function uniqueEmail()
        {
            if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) ) {
                if('xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])){
                    $this->email = $_POST['email'];
                    $sql = "SELECT count(*) FROM users WHERE email = '".$this->email."'";
                    $count = mysqli_fetch_row($this->db->query($sql))[0];
                    if ($count) {
                        echo "1";
                    }else{
                        echo "0";
                    }
                }else{
                    echo "hello world";
                }
            }else{
                echo "hello world";
            }
        }
    
  • 检测验证码

        public function checkCode()
        {
          //判断输入的验证码是否与session中的验证码一致
            if ($this->code != $_SESSION['code']) {
                echo "<script>alert('Verification code is not correct.please try again!');history.go(-1);</script>";
                exit();
            }
        }
    
  • 检测邮箱格式(正则)

        public function checkEmail()
        {
            $pattern = "/^([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)$/i";
            if (!preg_match($pattern,$this->email)) {
                echo "<script>alert('Email format incorrect.please try again!');history.go(-1);</script>";
                exit();
            }
        }
    
  • 用户名格式(长度)

        public function checkName()
        {
            $length = strlen($this->username);
            if (trim($this->username) == '' || $length < 2 || $length > 20) {
                echo "<script>alert('UserName format incorrect.please try again!');history.go(-1);</script>";
                exit();
            }
        }
    
  • 检测密码

        public function checkPwd(){
        //密码不能为空格,长度介于6至20之间
            if (trim($this->pwd) == '' || strlen($this->pwd) < 6 || strlen($this->pwd) > 20) {
                echo "<script>alert('PassWord format incorrect.please try again!');history.go(-1);</script>";
                exit();
            }
          //确认密码必须与密码保持一致
            if ($this->pwd != $this->con) {
                echo "<script>alert('Confirm password do not match.please try again!');history.go(-1);</script>";
                exit();
            }
        //加密密码
            $this->pwd = md5($this->pwd);
        }
    
  • 执行注册操作

        public function doRegister()
        {
            $this->email = $_POST['email'];        //接受参数
            $this->username = $_POST['username'];
            $this->code = $_POST['code'];
            $this->pwd = $_POST['password'];
            $this->con = $_POST['confirm'];
            $this->checkCode();    //检测数据
            $this->checkPwd();
            $this->checkName();
            $this->checkEmail();
            $sql = "INSERT INTO users (username, email, password) VALUES ('".$this->username."','".$this->email."','".$this->pwd."')";
            $result = $this->db->query($sql);        //将数据录入数据库
            if ($result) {
                $this->db->close();        //不要忘记关闭数据库连接
                echo "<script>alert('Successful registration, please log in!');location.href = '/';</script>";
                exit();
            }else{
                echo $this->db->error;
                exit();
            }
        }
    

    看到这里,你可能会有些疑惑,前端的输入数据我们已经通过 js做了格式检测,,为什么这里还要做相同的检测?

    这涉及到一点网络安全的知识。设计web应用时,一定要牢住的一条准则就是:永远不要相信客户端的输入 !前端 js 检测设计得再好,在有技术基础的人面前,都形同虚设。所以,不要指望你写的那点 js 能真正对数据过滤有多大的作用,那只适用于普通用户和技术小白而已。

  • 判断请求数据类型

    如果你对注册页面还有印象的话,应该知道,我们提交的数据可以有三种类型。其中两种是通过 Ajax 提交,一种是通过表单页面提交,所以在设计的时候,我添加了一个 type 的参数以区别是哪种数据的提交。所以我们在执行相关操作之前需要加以判断:

    $reg = new Register();    //先实例化注册类
    switch ($_POST['type']) {    //根据传递的type执行对应操作
        case 'name':    //ajax方式提交的用户名唯一性检测
            $reg->uniqueName();
            break;
        case 'email':    //ajax方式提交的邮箱唯一性检测
            $reg->uniqueEmail();
            break;
        case 'all':        //通过表单提交的注册操作
            $reg->doRegister();
            break;
        default:
            echo "hello world";
            break;
    }
    

2.3 前端修改

  • 修改 js 脚本

    将用户名和邮箱的 ajax 请求地址稍作修改:

    //用户名唯一性检测
    $.post('admin/Register.php', {username: $(this).val(),type:'name'}, function(data, textStatus, xhr) {
      *****
    }
    //邮箱唯一性检测
    $.post('admin/Rregister.php', {email: $(this).val(),type: 'email'}, function(data, textStatus, xhr) {
      *****
    }
    
  • 修改注册表单提交地址

    <form action="admin/Register.php" method="post" accept-charset="utf-8" class="form-horizontal">
    
  • 向注册表单添加一项 type 的输入标签,表示这是注册表单提交:

    <input type="hidden" name="type" value="all">
    

三、实现登陆功能

和注册功能类似,登陆功能也可以封装成为一个登陆类,其中主要的方法有以下几个:

  • 检查验证码
  • 检查邮箱格式和密码格式
  • 检查用户是否存在
  • 执行登陆操作

3.1 封装登陆类

  • 首先在 admin 目录下建立一个 Login.php 作为登陆处理文件。定义类,添加属性和构造方法:

    <?php 
    /**
    * login
    */
    class Login
    {
        private $email;    //登陆邮箱
        private $password;    //登陆密码
        private $code;    //验证码
        function __construct()
        {
            if (!isset($_POST['login'])) {    //非 post 方式提交不被接受
                echo "<script>alert('You access the page does not exist!');history.go(-1);</script>";
                exit();
            }
            require '../config.php';    //引入配置文件
            $this->email = $_POST['email'];
            $this->password = $_POST['password'];
            $this->code = $_POST['code'];
        }
    }
    
  • 检查登录邮箱格式(同上注册方法)
  • 检查密码格式

    //验证密码格式
    public function checkPwd()
        {
            if (!trim($this->password) == '') {
                $strlen = strlen($this->password);
                if ($strlen < 6 || $strlen > 20) {
                    echo "<script>alert('Password length of illegal.please try again!');history.go(-1);</script>";
                    exit();
                }else{
                    $this->password = md5($this->password);
                }
            }else{
                echo "<script>alert('Password cannot be blank.please try again!');history.go(-1);</script>";
                exit();
            }
        }
    
  • 检查验证码(同上注册)

  • 检查用户是否存在

    //数据库验证
    public function checkUser()
        {
            $db = new mysqli(DB_HOST,DB_USER,DB_PWD,DB_NAME) or die('数据库连接异常');
            $sql = "SELECT username FROM users WHERE email = '".$this->email."' and password = '".$this->password."'";
            $result = mysqli_fetch_row($db->query($sql))[0];
            if (!$result) {        //不存在,登陆失败
                echo "<script>alert('Email or password is incorrect.please try again!');history.go(-1);</script>";
                exit();
            }else{        //用户存在,登陆成功
                $_SESSION['user'] = $result;    //将用户名保存到 session 会话中
                $db->close();        //不要忘记关闭数据库连接
          //输出登陆成功信息,并跳转到主页(这里暂时为登陆和注册首页)
                echo "<script>alert('Login Success!');location.href = '/index.php'</script>";
                exit();
            }
        }
    
  • 执行以上操作:

        public function doLogin()
        {
            $this->checkCode();
            $this->checkEmail();
            $this->checkPwd();
            $this->checkUser();
        }
    
  • 调用登陆类

    $login = new Login();
    $login->doLogin();
    
  • 修改登陆表单提交地址

    <form action="admin/Login.php" method="post" accept-charset="utf-8" class="form-horizontal">
    

    四、功能测试

现在当你在注册页面输入用户名和邮箱时,便可做出动态响应。

输入有误:

register

注册成功:

register

登陆成功:

success

五、实验总结

到目前为止,简单的用户注册登陆功能就基本实现了。不过尚有很多不足。比如跳转的用户主页没有设计,还可以添加一个 记住我 的功能,页面查看的权限(是否登录),提高代码复用率等等。我们将会在下一个实验继续完善我们的项目。

功能完善

一、实验介绍

1.1 实验内容

本实验将对现有的程序代码进行优化完善,并增加新的功能以及访问权限的控制。

1.2 实验知识点

  • Html代码复用
  • 建立用户主页
  • 使用 cookie 实现 记住我 功能并且自动登录
  • 浏览页面访问限制

1.3 实验环境

  • sublime,一个方便快速的文本编辑器。点击桌面左下角: 应用程序菜单/开发/sublime。

  • MySQL,使用下面的命令开启服务:

    $ sudo service mysql start        # 使用上一个实验建立的数据库和数据表
    
  • 开启服务器:

    $ php -S localhost:80 #若不能启动成功,可将端口号换为 8080
    

1.4 源码下载

你可以通过以下方式获取本项目的全部示例代码:

wget http://labfile.oss.aliyuncs.com/courses/587/web.zip

二、前端优化

2.1 HTML复用

查看 index.php 代码,我们可以把整个网页分为三大区域:头部(header)、导航栏(nav)、主体(container)、底部(footer)。其中头部,导航栏和底部基本是每个页面都需要使用到的部分,只有主体的内容会发生变化。所以我们可以想办法把 header nav 和 footer 提取出来,作为单独的部件,index 页面通过引入文件的方式再把他们引入到自身。这样一来,提取出来的 header nav 和 footer 也可以供其他需要的页面使用而不用单独在每个页面上都手写一份。这种方式类似于模板布局。

首先在 public 目录下建立一个 layouts 的目录,用于存放提取出来的部件。在其下建立 header.php ,nav.php, footer.php。

将 index.php 中的相应代码剪切进来:

  • header.php

      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <meta name="description" content="">
        <meta name="author" content="">
        <link rel="icon" href="public/img/favicon.ico">
    
        <title>web page</title>
    
        <!-- Bootstrap core CSS -->
        <link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
    
        <!-- Custom styles for this template -->
        <link href= "public/css/start.css" rel="stylesheet">
        <link rel="stylesheet" type="text/css" href="public/css/footer.css">
      </head>
    
  • nav.php

        <nav class="navbar navbar-inverse navbar-fixed-top">
          <div class="container">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="#">ShiYanLou</a>
            </div>
            <div id="navbar" class="collapse navbar-collapse">
              <ul class="nav navbar-nav">
                <!-- 这里做了修改 -->
                <li class="active"><a href="#">Home</a></li>
                <li><a href="#Register" data-toggle="modal" data-target="#register">Register</a></li>
                <li><a href="#Register" data-toggle="modal" data-target="#login">Login</a></li>
              </ul>
            </div><!--/.nav-collapse -->
          </div>
        </nav>
    
  • footer.php

    <!-- 网页底部 -->
    <footer class="footer">
      <div class="container">
        <p class="text-muted">
        <h2><a href="http://www.shiyanlou.com" title="www.shiyanlou.com" style="color: blue;">www.shiyanlou.com</a></h2>
        </p>
      </div>
    </footer>
    

在 index.php 中引入对应文件:

<!DOCTYPE html>
<html lang="zh-CN">
  <!-- header部分 -->
  <?php require_once 'public/layouts/header.php' ?>

  <body>
  <!-- 导航栏 -->
  <?php require_once 'public/layouts/nav.php' ?>
  <!-- 页面主体内容 -->
    <div class="container">
        <!-- 表单部分(略) -->
       </div><!-- /.container -->

    <!-- 网页底部 -->
    <?php require_once 'public/layouts/footer.php'; ?>

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
    <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script src="public/js/check.js"></script>
  </body>
</html>

上面用到了一个函数:require_once ,那就有必要了解一下 requirerequire_onceincludeinclude_once 的区别。

require: 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告 E_WARNING),脚本会继续运行。

require_once:和 require 语句完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。

include:同上 require。

include_once :在脚本执行期间包含并运行指定文件。此行为和 include 语句类似,唯一区别是如果该文件中已经被包含过,则不会再次包含。如同此语句名字暗示的那样,只会包含一次。

再次打开网页,你会发现没有任何变化。说明我们的操作是成功的。

2.2 建立用户主页

我们往往更希望把用户的主页作为 index.php ,那我们就把之前那个首页文件重命名为 welcome.php 作为欢迎界面。新建一个 index.php 文件作为用户主页。我们还是可以沿用 welcome.php 的风格样式。所以,把 2.1 中的 index.php 的代码复制到现在的 index.php 中,把表单部分去掉,添加用户首页需要展示的内容:

<?php         
session_start();        //开启session会话
//此处添加PHP代码
?>
<!DOCTYPE html>
***
***
<!-- 页面主体内容 -->
    <div class="container">
      <div class="content">
          <div class="starter-template">
             <!-- 这里做了修改,其他地方自由发挥 -->
            <h1>Welcome To ShiYanLou</h1>
            <div class="jumbotron">
              <!-- 打印用户名 -->
              <h1>Hello, <?php echo $_SESSION['user']; ?>!</h1>
              <p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
              <p><a class="btn btn-primary btn-lg" href="http://www.shiyanlou.com" role="button">Learn more</a></p>
            </div>
          </div>  
      </div>
    </div><!-- /.container -->

这里我在主体内容中使用了 bootstrap 中的 巨幕 组件,并将登录用户的用户名打印出来,由于使用了 session ,所以我们需要在顶部首先开启 session:

index

2.3 浏览限制与细节处理

index.php 作为我们的用户主页,应该只能允许已登录的当前用户查看。如果用户未登陆,我们应该将其跳转到 welcome.php 让用户注册或登陆。如果用户已经登陆的,那么只能让他进入主页中去,而不能让他再次进入 welcome.php 中去登陆或者注册。因此我们需要对用户做一些浏览限制:

在 index.php 顶部的 php 代码段单继续添加如下代码:

//未登录,跳转至欢迎界面
if (!isset($_SESSION['user'])) {
  header('location:welcome.php');
  exit();   //防止继续执行
}

在 welcome.php 顶部添加 php 代码:

<?php 
    session_start();    //首先要开始 session
    //已登录,跳转至主页
  if (isset($_SESSION['user'])) {
    header('location:index.php');
    exit();
  }
 ?>

细心的你可能已经注意到了,在我们的用户主页的导航栏上,还有 register 和 login 的按钮,这是不应该在主页上出现的。所以我们需要修改导航栏部分的展示效果,编辑 /public/layouts/nav.php :

    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Menu</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="/">ShiYanLou</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
          <li class="active"><a href="#">Home</a></li>
          <?php 
              if (!isset($_SESSION['user'])) {
           ?>
            <li><a href="#Register" data-toggle="modal" data-target="#register">Register</a></li>
            <li><a href="#Register" data-toggle="modal" data-target="#login">Login</a></li>
            <?php
               }else{ 
             ?>
              <li><a href="http://www.shiyanlou.com" >Learn</a></li>
              <li><a href="admin/Logout.php" >Logout</a></li>
            <?php
               } 
             ?>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

我在上面的导航栏部分,添加了判断条件,如果已经登陆进入了用户主页,则像是 Learn 和 Logout 按钮。否则用户现在处于欢迎界面,就应该显示 Register 和 Login 按钮。

这里需要使用登出的方法,所以我们现在应该在 admin 新建一个 Logout.php 文件来完成登出的功能。

Logout.php:

<?php 
session_start();
unset($_SESSION['user']);
echo "<script>alert('You hava logouted!');location.href = '/welcome.php'</script>";

现在,我们的细节和浏览限制已经完成了。如果你还觉得有什么不足之处,你可以继续完善。

æ­¤å¤è¾å¥å¾ççæè¿°

 æ­¤å¤è¾å¥å¾ççæè¿°

三、实现记住我功能

我们经常在登陆的时候会看到这个选项,使用这个选项,只要不清除浏览器数据,下次访问网站时可以自动登陆,这是非常方便的一个功能。之所以这个功能放到这里来实现,是因为这个选项不是必须的,对于一些对安全要求比较高的网站,是不提供这个功能。比如淘宝等。使用 cookie 实现自动登录具有一定的安全风险,如果在真实的项目中对 cookie 的处理一定要谨慎,对于一般的网站则不需要考虑太多。我们之前实现的那些功能都是比较通用的。接下来我们可以把自动登录的功能加入到我们的项目中。

  • 首先,在登陆表单添加 Remember Me 选项(默认 yes):

    <div class="form-group">
      <label for="password" class="col-sm-4 control-label">Remember Me:</label>
      <div class="col-sm-3">
        <label class="checkbox-inline">
          <input type="radio" name="rem" id="yes" value="1" checked> Yes
        </label>
        <label class="checkbox-inline">
          <input type="radio" name="rem" id="no" value="0"> No
        </label>
      </div>
    </div>
    
  • 其次,修改后台登陆处理程序:

    private $rem;    //添加记住我私有属性
    *****
    $this->rem = $_POST['rem'];        //构造方法中获取记住我参数
    
    //在登陆成功之后,跳转页面之前添加判断,若 记住我,则将标识保存到session中
    if ($this->rem == 1) {
      $_SESSION['rem'] = '1';
    }
    

    这里可能有人会有疑惑,为什么不在这里直接使用 setcookie() 函数,而是保存到session中。如果你在这里设置了cookie,打开浏览器,使用开发者工具查看网络请求,你就会发现,从登陆成功再到跳转至用户主页的过程中,访问 index.php 的请求 header 部分中的 Request Headers 中包含了你刚才设置的 cookie ,而不是在 Response Header 中,很明显,当我们关闭了浏览器,这个 cookie 就不存在。更多的关于 response cookie 和 request cookie 的区别,可以到网上去查阅相关的资料。

  • 生成 cookie 并自动登陆:

    修改 index.php 上方 php 代码:

    if (!isset($_SESSION['user'])) {    //未登录
      if (isset($_COOKIE['user'])) {    //是否存在 cookie
        $_SESSION['user'] = $_COOKIE['user'];    //存在,自动登陆
      }else{
        header('location:welcome.php');     //不存在,跳转至欢迎界面
      }
    }
    if (isset($_SESSION['rem'])) {        //如果 session 中有 记住我 的标识,则在本页面生成用户名的 cookie,有效时间为一个小时(可以调节)
      setcookie('user',$_SESSION['user'],time()+3600);
      unset($_SESSION['rem']);    //清除本条session
    }
    
  • 清除 cookie :

    既然上面生成了 cookie ,那么当我们进行登出操作时,就应该将其清理掉。所以在 Logout.php 中添加一句代码:

    unset($_COOKIE['user']);
    

    OK !现在我们的项目已经实现了记住我 这样一个功能。基本上完成了本次项目的全部功能。

四、实验总结

本课程通过四次实验操作,带领大家从页面制作到最后的功能完善,既学到了前端的知识,也对后端技术有了一定的了解。这也是成为一名合格的PHP程序员所必须掌握的本领。相信通过本次课程的学习,大家以后自己搭建网站的时候,可以很轻松的实现用户注册登陆模块的功能。当然,因为作者的能力有限,文档中难免会有这样那样的不足之处,一些构思或者实现方法可能不是最优的,也欢迎各位在课程评论中指出。

发布了114 篇原创文章 · 获赞 98 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/github_39533414/article/details/84898432