在php中,许多函数或方法的形参为callable
类型, 这时,可以向这个参数传入具名函数或匿名函数。其中,匿名函数是php闭包的表示形式,为一个Closure类的实例。
下面,通过例子来演示闭包的创建和使用。
//闭包类
Closure {
/*方法*/
__construct ( void )
public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ])
public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])
}
一、创建闭包,与对象或类绑定来访问类成员
1. 创建闭包
//文件名 closure\closureCreate.php
namespace closure;
use Closure;
function closureCreate(string $name): Closure
{
//创建闭包
return function (string $var) use ($name) {
return sprintf('%s%s',$name,$var);
};
}
$closure = closureCreate('php中文网');
//使用闭包
echo $closure('欢迎您!');
结果
2. 将闭包与对象或类绑定来访问类成员
<?php
//文件 closure\closureAccess.php
namespace closure;
use Closure;
class closureAccess
{
public $publicVar = '';
public static $staticVar = '';
private $privateVar = '';
private static $privateStaVar = '';
protected $protectedVar = '';
protected static $protectedStaVar = '';
//取不存在的值时
public function __get(string $name): string
{
//如果非静态
if (isset($this->$name)) {
return $this->$name;
}
//如果静态
if (isset(static::${$name})) {
return static::${$name};
}
}
//设置不存在的值时
public function __set(string $name, $value): void
{
//如果非静态
if (isset($this->$name)) {
$this->$name = $value;
}
//如果静态
if (isset(static::${$name})) {
static::${$name} = $value;
}
}
//把对象当字符访问时
public function __toString(): string
{
return <<<DOC
publicvar = {$this->publicVar}</br>
staticVar = {$this->staticVar}</br>
privateVar = {$this->privateVar}</br>
privateStaVar = {$this->privateStaVar}</br>
protectedVar = {$this->protectedVar}</br>
protectedStaVar = {$this->protectedStaVar}</br>
DOC;
}
}
//匿名函数,闭包
$setter = function (string $publicVar, string $staticVar, string $privateVar, string $privateStaVar, string $protectedVar, string $protectedStaVar): void
{
//绑定 public
$this->publicVar = $publicVar;
//绑定 public static变量
static::$staticVar = $staticVar;
//绑定private变量
$this->privateVar = $privateVar;
//绑定private static变量
$this->privateStaVar = $privateStaVar;
//绑定protected 变量
$this->protectedVar = $protectedVar;
//绑定protected static 变量
$this->protectedStaVar = $protectedStaVar;
};
$closureAccess = new closureAccess();
//既有实例也有类,双绑定
// $closure=$setter->bindTo($closureAccess, closureAccess::class);
$closure = Closure::bind($setter, $closureAccess, closureAccess::class);
$closure('a', 'b', 'c', 'd', 'e', 'f');
echo $closureAccess;
结果
编程过程中,常出现很多的错误和异常,php有专门处理错误和异常的类(Exception类),可自定义该类的__toString()方法,自定义错误或异常的输出.
二、创建自定义异常类,实现用户登录与验证流程的异常处理
1. 自定义异常类
//文件src\login\LoginException.php;
namespace src\login;
use Exception;
class LoginException extends Exception
{
public function __construct($message,$previous)
{
parent :: __construct($message, $previous);
}
public function __toString(): string
{
return <<<DOC
<table border="1" cellsapcing="0" cellpadding="5">
<tr bgcolor="wheat">
<th>错误信息</th>
<th>代码</th>
<th>文件</th>
<th>行号</th>
</tr>
<tr>
<td>$this->message</td>
<td>$this->code</td>
<td>$this->file</td>
<td>$this->line</td>
</tr>
</table>
DOC;
}
}
2.用户登录
<?php
//文件 src\login\LoginView.php
namespace src\login;
class LoginView
{
private $action;
private $checkAction;
private $checkStyle;
public function __construct(string $action)
{
$this->action = $action;
}
//设置验证请求的内容: ....?action=login;
public function setCheckStyle(string $checkAction, string $checkStyle): void
{
$this->checkAction = $checkAction;
$this->checkStyle = $checkStyle;
}
public function loginSimple(): string
{
return <<<DOC
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h3>用户登录</h3>
<form action="{$this->action}?{$this->checkAction}={$this->checkStyle}" method="post">
<div>
<label for="name">用户名</label>
<input type="text" id='name' name='name'>
</div>
<div>
<label for="pssword">密码</label>
<input type="password" id='password' name='password'>
</div>
<button>提交</button>
</form>
</body>
</html>
DOC;
}
}
3.数据库准备
<?php
//文件 src\login\LoginModel.php;
namespace src\login;
use mysqli;
require 'LoginAutoload.php';
class LoginModel
{
private $mysqli;
public function __construct(string $host, string $user, string $password, string $dbname)
{
$this->mysqli = new mysqli($host, $user, $password, $dbname);
}
public function __set(string $name, $value)
{
$this->{$name} = $value;
}
//从数据库查询数据
public function select()
{
try {
$sql = 'SELECT ' . $this->fields . ' FROM ' . $this->table . (($this->condition) ? ' WHERE ' . $this->condition : NULL) . ';';
//处理查询异常
if (!$this->mysqli->query($sql)) throw new LoginException($sql . '<br>查询语句有误', 303);
$result = $this->mysqli->query($sql);
return $result->fetch_all(MYSQLI_ASSOC);
} catch (LoginException $e) {
echo $e;
}
}
}
4.登录验证
<?php
//文件 src\login\LoginControl;
namespace src\login;
require 'LoginAutoload.php';
class LoginControl
{
private $loginModel;
private $callback;
public function __construct(LoginModel $loginModel)
{
$this->loginModel = $loginModel;
}
//设置回调函数用于验证
public function setCallback(callable $callback): void
{
$this->callback = $callback;
}
//验证来源
public function checkUrl(): bool
{
$currentUrl = basename(filter_input(INPUT_SERVER, 'SCRIPT_NAME'));
//设置查询条件
$this->loginModel->condition = $this->loginModel->fields . '=\'' . $currentUrl . '\'';
try {
if (!($this->loginModel->select())) throw new LoginException('非法来源', 101);
echo '合法来源';
return true;
} catch (LoginException $e) {
echo $e;
return false;
}
}
//检查请求内容
public function checkAction($checkAction): bool
{
$action = filter_input(INPUT_GET, $checkAction, FILTER_SANITIZE_STRING);
//设置查询条件
$this->loginModel->condition = $this->loginModel->fields . '=\'' . strtolower($action) . '\'';
//如果请求内容不匹配,提示
try {
if (!($this->loginModel->select())) throw new LoginException('<br>无需验证', 305);
echo '<br>须验证:' . $checkAction;
return true;
} catch (LoginException $e) {
echo '<br>' . $e->getMessage();
return false;
}
}
//验证POST请求
public function checkPost(): bool
{
$check = function ($res) {
try {
//如果验证条件有异常
if (!$res) throw new LoginException('验证不通过', 102);
//设置cookie;
setcookie('user', $res);
exit('<script>alert("验证通过");</script>');
} catch (LoginException $e) {
echo $e;
exit('<script>alert("' . $e->getMessage() . '");</script>');
}
};
//请求是否合法
try {
if (!(filter_input(INPUT_SERVER, 'REQUEST_METHOD') === 'POST')) throw new LoginException('<br>非法请求', 101);
echo "<br>合法请求";
$check(call_user_func_array($this->callback, [$this->loginModel]));
return true;
} catch (LoginException $e) {
echo $e;
return false;
}
}
}
//数据库
$localhost = 'db.io';
$user = 'root';
$password = 'root';
$dbname = 'phpedu';
$loginModel = new LoginModel($localhost, $user, $password, $dbname);
$loginModel->fields = '`url`';
$loginModel->table = '`urls`';
$loginControl = new LoginControl($loginModel);
//验证来源是否合法
//SELECT `url` FROM `urls` WHERE `url`='LoginControl.php';
if (!$loginControl->checkUrl()) return;
//检查是否需要验证login
//LoginContro.php?action=login
$loginModel->fields = '`action`';
$loginModel->table = '`actions`';
if (!$loginControl->checkAction('action')) return;
//验证`name`和`password`;
$loginModel->fields = '`name`, `password`';
//验证`name`和`password`是否在数据库中
//传入验证内容的匿名函数,
$loginControl->setCallback(function (LoginModel $mysqli)
{
$name = filter_input(INPUT_POST, 'name');
$password = sha1(filter_input(INPUT_POST, 'password'));
//查找 SELECT `password` FROM `users` WHERE `name`='小龙女';
$mysqli->fields = "`password`";
$mysqli->condition = "`name`='{$name}'";
$mysqli->table = '`users`';
//处理查询异常
try {
if (!$mysqli->select()) throw new LoginException('查询失败', 305);
foreach ($mysqli->select() as $val) {
if ($val['password'] === $password) {
return $name;
}
}
} catch (LoginException $e) {
echo $e;
}
});
//验证POST请求,
//如果`name`和`password`在数据库中,则setcookie,否则提示验证失败
$loginControl->checkPost();
5.自动加载
<?php
//文件login/LoginAutoload.php
namespace src\login;
spl_autoload_register(function($class)
{
$prefix=__DIR__;
$arr=explode("\\",$class);
$file=$prefix."\\". $arr[count($arr)-1] . '.php';
$file = str_replace("\\", DIRECTORY_SEPARATOR, $file);
file_exists($file) ? require $file : "文件不存在,加载失败";
});
6.调用
<?php
//文件 src\login\LoginMain.php;
namespace src\login;
require 'LoginAutoload.php';
$loginView=new LoginView('LoginControl.php');
$loginView->setCheckStyle('action','login');
echo $loginView->loginSimple();
用到的数据库phpedu
3个表: users
, urls
, actions
1.users
2.urls
3.actions
结果
1. 密码错误时
2. 密码正确时
3. 用户不存在时
总结
1.闭包:闭包可以将一个执行环境(父级中的变量/状态)封闭到匿名函数中;常用于作为函数/方法的回调参数。闭包是一个对象,是Closure
类的实例;可以把外部数据封闭到闭包中保存,也可以将闭包绑定到对象/类上,实现对属性的更新操作;
2.异常:与错误机制相比,异常处理机制主动抛出,更加主动和灵活;有时异常并不一定发生了错误,所以异常应用范围更广;通常并不会直接使用系统的异常类,而是自定义一个异常类