//身份证号码
$str = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
//护照
$str = "/^1[45][0-9]{7}|([P|p|S|s]\d{7})|([S|s|G|g]\d{8})|([Gg|Tt|Ss|Ll|Qq|Dd|Aa|Ff]\d{8})|([H|h|M|m]\d{8,10})$/";
//回乡证号码
$str = "/(H|M)(\d{10})$/";
用ASCII统计字符串长度
/**统计字符串长度
* @param $str
* @param string $charset
* @return int
*/
function v_strlen($str,$charset='utf-8'){
$n = 0; $p = 0; $c = '';
$len = strlen($str);
if($charset == 'utf-8') {
for($i = 0; $i < $len; $i++) {
$c = ord($str{$i});
if($c > 252) {
$p = 5;
} elseif($c > 248) {
$p = 4;
} elseif($c > 240) {
$p = 3;
} elseif($c > 224) {
$p = 2;
} elseif($c > 192) {
$p = 1;
} else {
$p = 0;
}
$i+=$p;$n++;
}
} else {
for($i = 0; $i < $len; $i++) {
$c = ord($str{$i});
if($c > 127) {
$p = 1;
} else {
$p = 0;
}
$i+=$p;$n++;
}
}
return $n;
}
PHP防盗链思路
闲来无事,突然想到用PHP做防盗链应该怎么做,目前我只知道可以通过web服务器(如nginx)通过请求header中的referer来判断
如果用PHP应该怎么做呢?
思路:
- 所有文件资源访问路径都是由服务端返回,前端基于此来访问文件,因此,文件的所有访问由服务端控制
- 服务端返回文件名称时可以给文件路径加密(注意此加密是可逆的),例如自定义一个key + 真实文件名进行加密
- 服务端接收到加密的文件访问时通过给定的key进行解密,获得真实的文件名,然后服务器端直接输出文件流
以图片文件为例
<?php
//接收请求参数
$param = $_GET['u'];
//这一步是将文件路径名称和key加密 然后给客户端(我这里的key 是时间戳 每个整分变换一次)
//为例方便演示我这里的加密用的是base64
/*$date = date('Y-m-d H:00');
$pk = strtotime($date).'-lnmp.gif';
$name = 'lnmp.gif';
$img = base64_encode($pk);
*/
list($key,$img) = explode('-', base64_decode($param));
//TODO 获取到key以后可以和时间戳对比
if(!is_file($img)) {
echo '文件资源不存在';die;
}
//获取文件信息
$info = getimagesize($img);
//获取文件后缀
$imgExt = image_type_to_extension($info[2], false);
$fun = "imagecreatefrom{$imgExt}";
$imgInfo = $fun($img);
//获取图片的 MIME 类型
$mime = image_type_to_mime_type(exif_imagetype($img));
//指定响应的Content-Type 这一步很重要,否则不能输出文件对应的type
header('Content-Type:'.$mime);
$quality = 100;
if($imgExt == 'png') $quality = 9; //输出质量,JPEG格式(0-100),PNG格式(0-9)
$getImgInfo = "image{$imgExt}";
$getImgInfo($imgInfo, null, $quality); //2.将图像输出到浏览器或文件。如: imagepng ( resource $image )
//直接输出
echo imagedestroy($imgInfo);
访问正常的文件路径,暴漏了真实的文件路径
访问加密后的文件,文件真实路径未知
访问错误的文件,提示错误
其它
- key建议用时间戳
- 设定间隔时间,每次生成新的加密路径给客户端
- 同时访问资源时验证时间戳是否超过设定间隔时间
- 由于时间戳变化,每次加密后的文件路径不同,旧有的访问必定失效
依赖注入
依赖注入
- 简单来说,就是依赖于某个东西,然后给依赖者注入所依赖的东西来解决这个问题
例如:
- 商品有积分兑换商品和抢购商品
- 订单结算时,订单结算对象依赖于商品表的数据模型
- 针对不同商品模型对商品数据的处理,可以给订单结算对象注入不同的商品表模型
代码演示demo
- 积分商品模型
/**
* Class Integral
* @package 积分商品模型
*/
class Integral
{
/**
* 积分商品计算方法
*/
public function handle()
{
return '该商品只需要'.$this->price();
}
public function price()
{
return '积分:500';
}
}
- 抢购商品模型
/**
* Class Shop
* @package 抢购商品模型
*/
class Shop
{
public function handle()
{
return '该商品只需要'.$this->price();
}
public function price()
{
return '¥:122元';
}
}
- 订单收银台
/**
* Class Order
* @package 订单处理
*/
class Order
{
/**
* 收银台
*/
public static function pay($goods)
{
return $goods->handle();
}
}
使用方法
//如果商品是积分
dump(Order::pay((new Integral)));
//如果是抢购商品
dump(Order::pay((new Shop)));
运行截图
最后:
- 其实依赖注入早已经在我们的代码中实现
- 其功能类似于给函数传入一个回调函数
注册树模式
注册树模式
- 顾名思义,是将对象挂载到一个对象书上
- 需要使用某个对象时,从对象树上加载该对象即可
- 因此,对象树一定是全局的
代码demo
/**
* Class Tree
* @package 注册树模式
*/
class Tree
{
/*树枝*/
protected static $objects = null;
/*将对象挂载到树干上*/
public static function set($key,$object)
{
self::$objects[$key] = $object;
}
/*从树干上获取对象*/
public static function get($key)
{
if (!isset(self::$objects[$key])) {
//self::set($key,$className);
self::$objects[$key] = new $key;
}
return self::$objects[$key];
}
/*删除对象*/
public static function _unset($className) {
unset(self::$objects[$className]);
}
}
使用方法
$test = new Test;
Tree::set('test',$test);
//Tree::set('b',B); 如果有其他类
//Tree::set('a',A); 如果有其他类
dump(Tree::get("test")->getData());
//dump(Tree::get("a")->getData());
//dump(Tree::get("b")->getData());
测试的Test类
class Test
{
public function getData()
{
return '我是test类 获取到了';
}
}
运行截图
单例模式
单例模式
- 私有化构造方法,禁止外部实例化对象
- 声明一个静态属性,用于保存类的实例
- 声明一个静态方法,用声明的静态属性实例化对象
单例模式的最终目的
- 将对象实例化后保存到静态属性中,接下来的每次调用该对象时都是调用该静态属性
- 而不是重新实例化该对象
- 要知道,每次实例化一个对象时都是需要消耗一定的内存的
演示demo
class Singleton
{
public static $instance = null;
private function __construct()
{}
public static function getInstance()
{
if (!self::$instance){
self::$instance = new self();
}
return self::$instance;
}
public function getData()
{
return '获取到了';
}
}
使用方法
dump(Singleton::getInstance()->getData());
输出结果
理解PHP中的ArrayAccess
PHP内置对象 ArrayAccess 是一个 提供像访问数组一样访问对象的能力的接口
该对象内置的以下四个必须实现的方法
/*检查获取的属性是否存在*/
public function offsetExists($offset);
/*获取一个属性值*/
public function offsetGet($offset);
/*设置一个属性值*/
public function offsetSet($offset, $value);
/*删除一个属性值*/
public function offsetUnset($offset);
演示demo
class Test implements \ArrayAccess
{
private $data = ['title'=>'test_ArrayAccess'];
/*检查获取的属性是否存在*/
public function offsetExists($offset){
return isset($this->data[$$offset]);
}
/*获取一个属性值*/
public function offsetGet($offset){
return $this->data[$offset];
}
/*设置一个属性值*/
public function offsetSet($offset, $value){
$this->data[$offset] = $value;
}
/*删除一个属性值*/
public function offsetUnset($offset){
unset($this->data[$offset]);
}
public static function index()
{
$obj = new Test();
dump($obj['title']);
}
}
输出结果
文件锁
打开文件
fopen(file,chmod)
file:文件名称
chmod:打开权限
"r" (只读方式打开,将文件指针指向文件头)
"r+" (读写方式打开,将文件指针指向文件头)
"w" (写入方式打开,清除文件内容,如果文件不存在则尝试创建之)
"w+" (读写方式打开,清除文件内容,如果文件不存在则尝试创建之)
"a" (写入方式打开,将文件指针指向文件末尾进行写入,如果文件不存在则尝试创建之)
"a+" (读写方式打开,通过将文件指针指向文件末尾进行写入来保存文件内容)
"x" (创建一个新的文件并以写入方式打开,如果文件已存在则返回 FALSE 和一个错误)
"x+" (创建一个新的文件并以读写方式打开,如果文件已存在则返回 FALSE 和一个错误)
文件加锁/释放锁
flock(file,lock,block)
file:已经打开的文件资源
lock:锁的类型
LOCK_SH:共享锁(读锁)
LOCK_EX:独占锁定(排它锁,写锁)
LOCK_UN:解锁
LOCK_NB:如果希望在文件锁定时阻塞进程,那么需要加上该参数
block:设置为true的时候,锁定文件时,会阻止其他进程
关闭文件
flock(file)
file:打开的文件资源
自定义日志记录方法
/**
* [log 记录执行日志]
* @param [type] $str [description]
* @param [type] $count [description]
* @return [type] [description]
*/
public function log($str,$count)
{
$date = date('Y-m-d H:i:s');
$text = '-------------------------------------------------------------------'.PHP_EOL;
if ($count > 0) {
$text .= "|🐉[ {$str},时间:{$date},row:{$count} ]🐉|".PHP_EOL;
}else{
$text .= "|🐍[ {$str},时间:{$date},row:{$count} ]🐍|".PHP_EOL;
}
$dir = RUNTIME_PATH.'Timing';
if (!is_dir($dir)) {
mkdir($dir);
}
$file_name = $dir.'/Timing_'.date('Y_m_d').'.log';
file_put_contents($file_name,$text,FILE_APPEND);
}
观察者模式
<?php
/**
* @Author: Fyang
* @Email: admin@fyang.vip
* @Date: 2020-08-06 18:07:50
* @Last Modified by: 峰扬
* @Last Modified time: 2020-08-06 18:34:24
* @Project_name: test
* Description : 观察者模式
*/
/**
* 登录完成后需要:
* 1、发送短信
* 2、发送邮件
* 3、推送活动
* 4、登录统计次数
*/
/**
* 被观察者抽象类
* 预定义一个准则类(接口) 所有后续完成的业务都需要继承此类(按照此准则工作)
*/
interface Subject{
public function register(Observer $observer);
public function notify();
}
/**
* 被观察者
* 实现被观察者抽象类
*/
class Action implements Subject{
public $_observers = [];
public function register(Observer $observer){
$this->_observers[] = $observer;
}
public function notify(){
foreach ($this->_observers as $observer) {
$observer->watch();
}
}
}
/**
* 观察者接口
* 预定义一个准则类(接口) 所有后续完成的业务都需要继承此类(按照此准则工作)
*/
interface Observer{
//具体实现方法
public function watch();
}
/**
* 观察者
* 1、发送短信
*/
class Sms implements Observer{
public function watch(){
echo "this Sms class".PHP_EOL.PHP_EOL;
}
}
/**
* 观察者
* 2、发送邮件
*/
class Email implements Observer{
public function watch(){
echo "this Email class".PHP_EOL.PHP_EOL;
}
}
/**
* 观察者
* 3、推送活动
*/
class Activity implements Observer{
public function watch(){
echo "this Activity class".PHP_EOL.PHP_EOL;
}
}
/**
* 观察者
* 4、登录统计次数
*/
class Count implements Observer{
public function watch(){
echo "this Count class".PHP_EOL.PHP_EOL;
}
}
$action = new Action;
$action->register(new Sms());
$action->register(new Email());
$action->register(new Activity());
$action->register(new Count());
print_r($action->notify());