用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;
}

Windows Terminal ssh解决超时断开的问题

在window的命令行里面用ssh连接远程终端超时会自动断开的问题

  1. 打开 window Terminal 或 cmd 进去用户家目录
  2. 家目录下面有通过ssh命令连接过的记录 .ssh文件夹(如果没有 选择查看->勾选隐藏的项目)
  3. 进去.ssh目录
  4. 新建 config 文件(不要有任何后缀)
  5. 输入 ServerAliveInterval 60 (表示间隔60秒发送一次数据)并保存
Windows PowerShell
版权所有 (C) Microsoft Corporation。保留所有权利。

尝试新的跨平台 PowerShell https://aka.ms/pscore6

PS C:\Users\Administrator> ls


    目录: C:\Users\Administrator


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2020/11/15     17:38                .ssh
d-----        2020/11/11     14:24                .translation
d-----        2020/11/15     16:57                .VirtualBox
d-r---         2020/11/6     13:21                3D Objects
d-r---         2020/11/6     13:21                Contacts
d-r---        2020/11/13     12:13                Desktop
d-r---        2020/11/12     17:12                Documents
d-r---        2020/11/14     14:03                Downloads
d-r---         2020/11/6     13:22                Favorites
d-r---         2020/11/9     14:00                Links

PS C:\Users\Administrator> cd .\.ssh\
PS C:\Users\Administrator\.ssh> ls
PS C:\Users\Administrator\.ssh> cd > config
PS C:\Users\Administrator\.ssh> notepad .\config
PS C:\Users\Administrator\.ssh> cat .\config
ServerAliveInterval 60
PS C:\Users\Administrator\.ssh>

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

MySQL排除查询-排除查询一个表在另一个表中存在的数据

排除查询一个表在另一个表中存在的数据
例如:

  • 任务完成后列表中不再显示该任务
  • 任务领取后的记录表中存在的数据,在任务列表里不在显示
  • task 任务表、task_sign 任务记录表
SELECT
*
FROM
    `sz_task`
WHERE
    AND (
        SELECT 
            count( * ) AS num 
        FROM 
            sz_task_sign 
        WHERE 
            `sz_task`.id = `sz_task_sign`.task_id AND STATUS = '3'
        AND user_id = 1
    ) = 0 

注意:

  • 如果子查询中的 ( SELECT count( * ) AS num FROM sz_task_sign WHERE sz_task.id = sz_task_sign.task_id AND STATUS = '3' AND user_id = 1 ) = 1 则条件刚好相反

依赖注入

依赖注入

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

例如:

  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

一条MySQL语句统计多个字段

测试数据表demo

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for test_order
-- ----------------------------
DROP TABLE IF EXISTS `test_order`;
CREATE TABLE `test_order` (
  `id` int(11) NOT NULL,
  `order_no` varchar(32) DEFAULT NULL COMMENT '订单号',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `shop_id` int(11) DEFAULT NULL COMMENT '商家id',
  `status` tinyint(1) DEFAULT NULL COMMENT '订单状态 0待发货 1已发货 2已收货 3已评论 4申请退款',
  `create_time` int(10) DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of test_order
-- ----------------------------
INSERT INTO `test_order` VALUES ('1', 'aaaaaaaaa', '11', '111', '1', '1573041313');
INSERT INTO `test_order` VALUES ('2', 'bbbbbbbbb', '11', '222', '1', '1573041313');
INSERT INTO `test_order` VALUES ('3', 'ccccccccc', '11', '333', '2', '1573041313');
INSERT INTO `test_order` VALUES ('4', 'ddddddddd', '11', '222', '3', '1573041313');
INSERT INTO `test_order` VALUES ('5', 'eeeeeeeee', '11', '111', '4', '1573041313');
INSERT INTO `test_order` VALUES ('6', 'fffffffff', '11', '111', '3', '1573041313');
INSERT INTO `test_order` VALUES ('7', 'ggggggggg', '11', '222', '4', '1573041313');
INSERT INTO `test_order` VALUES ('8', 'hhhhhhhhh', '11', '111', '0', '1573041313');
INSERT INTO `test_order` VALUES ('9', 'iiiiiiiii', '11', '333', '0', '1573041313');
INSERT INTO `test_order` VALUES ('10', 'jjjjjjjjj', '11', '222', '1', '1573041313');

表截图

微信截图_20201013180108.png
查询语句

SELECT
    count( CASE WHEN STATUS = 0 THEN 1 END ) AS '待发货',
    count( CASE WHEN STATUS = 1 THEN 1 END ) AS '已发货',
    COUNT( CASE WHEN STATUS = 2 THEN 1 END ) AS '已收货',
    count( CASE WHEN STATUS = 3 THEN 1 END ) AS '已评论',
    count( CASE WHEN STATUS = 4 THEN 1 END ) AS '申请退款' 
FROM
    test_order;

运行截图

微信截图_20201013180006.png