public function getTree($list,$pid = 0,$level = 1)
{
$data = [];
foreach ($list as $k => $v) {
if ($v['pid'] == $pid) {
$v['level'] = $level;
$v['children'] = $this->getTree($list,$v['id'],($level + 1));
$data[] = $v;
}
}
return $data;
}
tp5命令行 添加守护进程方法
protected function configure()
{
$this->setName('pull_mqtt')
->setDescription('Mqtt Subscribe Workerman Service')
->addArgument('commands')
->addOption('daemon','d',null,'-d');
}
运行
php think pull_mqtt start -d
正则
//身份证号码
$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:打开的文件资源