1. 序列化和反序列化,sleep()和wakeup()函数
- sleep(): 在序列化对象时, 系统会调用sleep()方法, 它返回数组. 可以在__sleep()中确定哪些对象属性返回, 哪些不返回.
- wakeup(): 在反序列化对象时, 可以调用wakeUp()方法, 设置需要反序列化的字段和对反序列化后的属性值进行控制修改. 还可以调用对象方法执行部分业务逻辑.
interface Itf1
{
const CONST1 = 'const_from_interface';
}
trait Trait1
{
public static $tParam1 = 'static_param_from_trait';
public $tParam2 = 'param_from_trait';
private $tParam3 = 'private_param_from_trait';
}
class Demo1 implements Itf1
{
use Trait1;
private $cParam1 = 'param_in_class';
public $cParam2 = 'public_param';
public static $cParam3 = 'static_param_in_class';
public function func1()
{
echobr(__METHOD__);
}
/* sleep函数指定序列化时, 哪些熟悉被序列化 */
public function __sleep()
{
// 不序列化cParam2属性, 私有属性也可以通过__sleep()函数指定被序列化; 接口或trait中的属性, 常量等同理.
// 但是, 常量和静态变量的值并不被序列化, 所以CONST1, cParam3, tParam1在序列化字符串中的值是N(即NULL).
return ['CONST1', 'tParam1', 'tParam2', 'tParam3', 'cParam1', 'cParam3'];
}
/* wakeup函数在反序列化时, 可以对对象属性赋值, 也可以调用对象方法 */
public function __wakeup()
{
// 无法在wakeup中给静态属性赋值.
// self::$tParam1 = 'value_set_by_wakeup';
// self::$cParam3 = 'value_set_by_wakeup';
// 普通属性(public/protected/private均没有问题)
$this->cParam2 = '1234';
}
}
// 执行序列化
$objStr = serialize(new Demo1);
echobr($objStr);
/* result: O:5:"Demo1":6:{s:6:"CONST1";N;s:7:"tParam1";N;s:7:"tParam2";s:16:"param_from_trait";s:14:"Demo1tParam3";s:24:"private_param_from_trait";s:14:"Demo1cParam1";s:14:"param_in_class";s:7:"cParam3";N;} */
echobr();
// 执行反序列化
$obj = unserialize($objStr);
dumpbr($obj);
/* result: object(Demo1)#1 (7) { ["cParam1":"Demo1":private]=> string(14) "param_in_class" ["cParam2"]=> string(4) "1234" ["tParam2"]=> string(16) "param_from_trait" ["tParam3":"Demo1":private]=> string(24) "private_param_from_trait" ["CONST1"]=> NULL ["tParam1"]=> NULL ["cParam3"]=> NULL } */
2. 登录认证-自定义异常
/* 自定义登录异常类 */
class LoginException extends Exception
{
public function __construct($code, $message)
{
$this->code = $code;
$this->message = $message;
}
public function __toString()
{
return "异常编码: {$this->code}, 异常信息: {$this->message}, 异常文件: {$this->file}, 异常行号: {$this->line}";
}
}
class Login
{
private $username;
private $password;
public function __construct()
{
// 模拟有效的用户和密码分别是admin和123456
$this->username = 'admin';
$this->password = md5('123456');
}
/* 登录验证 */
public function loginCheck($username, $password)
{
try {
if (empty($username)) {
throw new LoginException('1', '用户名不能为空');
}
if (empty($password)) {
throw new LoginException('2', '密码不能为空');
}
if ($username != $this->username) {
throw new LoginException('3', '无效的用户名');
}
if (md5($password) !== $this->password) {
throw new LoginException('4', '密码不正确');
}
echobr('登录成功');
return true;
} catch (LoginException $le) {
echobr('登录失败');
echobr($le);
return false;
}
}
}
// 客户端调用
$login = new Login();
$username = null;
$password = null;
// 验证1
$login->loginCheck($username, $password);
/* result:
登录失败
异常编码: 1, 异常信息: 用户名不能为空, 异常文件: D:\phpstudy_pro\WWW\php11\PHP\0504\homework.php, 异常行号: 95
*/
$username = 'zhangsan';
// 验证2
$login->loginCheck($username, $password);
/* result:
异常编码: 2, 异常信息: 密码不能为空, 异常文件: D:\phpstudy_pro\WWW\php11\PHP\0504\homework.php, 异常行号: 98
*/
$password = '111111';
// 验证3
$login->loginCheck($username, $password);
/* result:
登录失败
异常编码: 3, 异常信息: 无效的用户名, 异常文件: D:\phpstudy_pro\WWW\php11\PHP\0504\homework.php, 异常行号: 101
*/
$username = 'admin';
// 验证4
$login->loginCheck($username, $password);
/* result:
登录失败
异常编码: 4, 异常信息: 密码不正确, 异常文件: D:\phpstudy_pro\WWW\php11\PHP\0504\homework.php, 异常行号: 104
*/
$password = '123456';
//验证5
if ($login->loginCheck($username, $password)) {
echobr('跳转到首页...');
}
/* result:
登录成功
跳转到首页...
*/
4. 匿名类的使用
4.1 匿名类使用场景1: 实现接口部分方法, 且仅用在当前项目中
interface ICalc
{
public function add($num1, $num2, ...$num3);
public function minus($num1, $num2);
public function multiply($num1, $num2, ...$num3);
public function division($num1, $num2);
}
class Salary
{
public $name = '';
// 基本工资
private $basePay = 0;
// 月绩效
private $projectBonus = 0;
// 其他补助
private $otherPay = 0;
public function __construct($name, $basePay = 0, $projectBonus =0, $otherPay = 0)
{
$this->name = $name;
$this->basePay = $basePay;
$this->projectBonus = $projectBonus;
$this->otherPay = $otherPay;
}
public function getSalary() {
return (new class() implements ICalc {
public function add($num1, $num2, ...$num3) {
$res = $num1 + $num2;
if(!empty($num3) && count($num3) > 0) {
foreach($num3 as $num) {
$res += $num;
}
}
return $res;
}
public function minus($num1, $num2){}
public function multiply($num1, $num2, ...$num3){}
public function division($num1, $num2){}
})->add($this->basePay, $this->projectBonus, $this->otherPay);
}
}
$salary = new Salary('张三', 3000, 8000, 2000);
echobr("{$salary->name}的月薪为:{$salary->getSalary()}");
/* result: 张三的月薪为:13000 */
4.2 匿名类应用场景2:用在函数/方法的参数中
function getSalary(object $tool, $basePay, $projectBonus, $otherPay) {
return $tool->getSalary($basePay, $projectBonus, $otherPay);
}
$salary = getSalary((new class() {
public function getSalary($basePay, $projectBonus, $otherPay) {
return $basePay + $projectBonus + $otherPay;
}
}), 3000, 8000, 2000);
echobr("工资为:{$salary}");
/* result: 工资为:13000 */
5. 学习心得
- 序列化和反序列化,做作业的时候同时试了一下,静态成员,常量成员是不能被序列化的,哪怕在__sleep()方法中把这些特殊成员也返回,序列化出来的字符串中,相应的值也是N(null);反序列化时也不能在__wakeup()中给静态成员和常量设置值。没有预习,可能文档中有说到,但现在踩坑了体会更深一些。
- 自定义异常,暂时还没发现其使用场景,作业中的实例,登录异常,其实可以用流程控制的方式,给前端返回json或序列化的信息,也能达到同样的目的。
- 匿名类,也遇到坑:在匿名类中设置的属性,并不能成功返回,最终只能取到NULL值。我猜测可能是因为匿名类执行完后,垃圾回收机制把匿名类对象回收了,这样对象里面的属性也就没有了。且暂时没有感受到使用匿名类带来的便利,示例感觉不用匿名类也可以,而且代码还更方便阅读。可能在实战中才能体会到其便利吧。