递归菜单分组

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);

访问正常的文件路径,暴漏了真实的文件路径
1.png

访问加密后的文件,文件真实路径未知
2.png
访问错误的文件,提示错误
3.png

其它

  • key建议用时间戳
  • 设定间隔时间,每次生成新的加密路径给客户端
  • 同时访问资源时验证时间戳是否超过设定间隔时间
  • 由于时间戳变化,每次加密后的文件路径不同,旧有的访问必定失效

依赖注入

依赖注入

  • 简单来说,就是依赖于某个东西,然后给依赖者注入所依赖的东西来解决这个问题

例如:

  1. 商品有积分兑换商品和抢购商品
  2. 订单结算时,订单结算对象依赖于商品表的数据模型
  3. 针对不同商品模型对商品数据的处理,可以给订单结算对象注入不同的商品表模型

代码演示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)));

运行截图

屏幕截图 2020-10-19.png

最后:

  • 其实依赖注入早已经在我们的代码中实现
  • 其功能类似于给函数传入一个回调函数

注册树模式

注册树模式

  1. 顾名思义,是将对象挂载到一个对象书上
  2. 需要使用某个对象时,从对象树上加载该对象即可
  3. 因此,对象树一定是全局的

代码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类 获取到了';
    }

}

运行截图

屏幕截图 2020-10-19 222043.png

单例模式

单例模式

  1. 私有化构造方法,禁止外部实例化对象
  2. 声明一个静态属性,用于保存类的实例
  3. 声明一个静态方法,用声明的静态属性实例化对象

单例模式的最终目的

  • 将对象实例化后保存到静态属性中,接下来的每次调用该对象时都是调用该静态属性
  • 而不是重新实例化该对象
  • 要知道,每次实例化一个对象时都是需要消耗一定的内存的

演示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());

输出结果
屏幕截图 2020-10-18 135729.png

理解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']);
    }

}

输出结果
屏幕截图 2020-10-18 133455.png

文件锁

打开文件

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:打开的文件资源