/**统计字符串长度
* @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连接远程终端超时会自动断开的问题
- 打开 window Terminal 或 cmd 进去用户家目录
- 家目录下面有通过ssh命令连接过的记录 .ssh文件夹(如果没有 选择查看->勾选隐藏的项目)
- 进去.ssh目录
- 新建 config 文件(不要有任何后缀)
- 输入 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);
访问正常的文件路径,暴漏了真实的文件路径
访问加密后的文件,文件真实路径未知
访问错误的文件,提示错误
其它
- 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 则条件刚好相反
依赖注入
依赖注入
- 简单来说,就是依赖于某个东西,然后给依赖者注入所依赖的东西来解决这个问题
例如:
- 商品有积分兑换商品和抢购商品
- 订单结算时,订单结算对象依赖于商品表的数据模型
- 针对不同商品模型对商品数据的处理,可以给订单结算对象注入不同的商品表模型
代码演示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']);
}
}
输出结果
一条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');
表截图
查询语句
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;
运行截图