首页
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
Search
1
【PHP】PHPoffice/PHPSpreadsheet读取和写入Excel
1,785 阅读
2
【Layui】控制页面元素展示隐藏
1,602 阅读
3
【Git】No tracked branch configured for branch master or the branch doesn't exist.
1,561 阅读
4
【PHP】PHP实现JWT生成和验证
1,483 阅读
5
【composer】composer常用命令
1,357 阅读
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
webman
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
Elasticsearch
登录
Search
标签搜索
PHP
函数
方法
类
MySQL
ThinkPHP
JavaScript
OOP
Layui
Web
Server
Docker
PHPSpreadsheet
PHPoffice
Array
设计模式
Nginx
Git
排序算法
基础
小破孩
累计撰写
256
篇文章
累计收到
13
条评论
首页
栏目
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
webman
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
Elasticsearch
页面
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
搜索到
252
篇与
的结果
2022-08-02
【设计模式】观察者设计模式
观察者模式是挺常见的一种设计模式,使用得当会给程序带来非常大的便利,使用得不当,会给后来人一种难以维护的想法。使用场景: 用户登录,需要写日志,送积分,参与活动 等使用消息队列,把用户和日志,积分,活动之间解耦合什么是观察者模式? 一个对象通过提供方法允许另一个对象即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦。看下面例子你就明白了!<?php /* 观察者接口 */ interface InterfaceObserver { function onListen($sender, $args); function getObserverName(); } // 可被观察者接口 interface InterfaceObservable { function addObserver($observer); function removeObserver($observer_name); } // 观察者抽象类 abstract class Observer implements InterfaceObserver { protected $observer_name; function getObserverName() { return $this->observer_name; } function onListen($sender, $args) { } } // 可被观察类 abstract class Observable implements InterfaceObservable { protected $observers = array(); public function addObserver($observer) { if ($observerinstanceofInterfaceObserver) { $this->observers[] = $observer; } } public function removeObserver($observer_name) { foreach ($this->observersas $index => $observer) { if ($observer->getObserverName() === $observer_name) { array_splice($this->observers, $index, 1); return; } } } } // 模拟一个可以被观察的类 class A extends Observable { public function addListener($listener) { foreach ($this->observersas $observer) { $observer->onListen($this, $listener); } } } // 模拟一个观察者类 class B extends Observer { protected $observer_name = 'B'; public function onListen($sender, $args) { var_dump($sender); echo "<br>"; var_dump($args); echo "<br>"; } } // 模拟另外一个观察者类 class C extends Observer { protected $observer_name = 'C'; public function onListen($sender, $args) { var_dump($sender); echo "<br>"; var_dump($args); echo "<br>"; } } $a = new A(); // 注入观察者 $a->addObserver(new B()); $a->addObserver(new C()); // 可以看到观察到的信息 $a->addListener('D'); // 移除观察者 $a->removeObserver('B'); // 打印的信息: // object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } } // string(1) "D" // object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } } // string(1) "D"
2022年08月02日
267 阅读
0 评论
0 点赞
2022-08-02
【设计模式】工厂设计模式
要是当操作类的参数变化时,只用改相应的工厂类就可以工厂设计模式常用于根据输入参数的不同或者应用程序配置的不同来创建一种专门用来实例化并返回其对应的类的实例。使用场景 :使用方法 new实例化类,每次实例化只需调用工厂类中的方法实例化即可。优点 :由于一个类可能会在很多地方被实例化。当类名或参数发生变化时,工厂模式可简单快捷的在工厂类下的方法中 一次性修改,避免了一个个的去修改实例化的对象。我们举例子,假设矩形、圆都有同样的一个方法,那么我们用基类提供的API来创建实例时,通过传参数来自动创建对应的类的实例,他们都有获取周长和面积的功能。例子<?php interface InterfaceShape { function getArea(); function getCircumference(); } /** * 矩形 */ class Rectangle implements InterfaceShape { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function getArea() { return $this->width* $this->height; } public function getCircumference() { return 2 * $this->width + 2 * $this->height; } } /** * 圆形 */ class Circle implements InterfaceShape { private $radius; function __construct($radius) { $this->radius = $radius; } public function getArea() { return M_PI * pow($this->radius, 2); } public function getCircumference() { return 2 * M_PI * $this->radius; } } /** * 形状工厂类 */ class FactoryShape { public static function create() { switch (func_num_args()) { case1: return newCircle(func_get_arg(0)); case2: return newRectangle(func_get_arg(0), func_get_arg(1)); default: # code... break; } } } $rect =FactoryShape::create(5, 5); // object(Rectangle)#1 (2) { ["width":"Rectangle":private]=> int(5) ["height":"Rectangle":private]=> int(5) } var_dump($rect); echo "<br>"; // object(Circle)#2 (1) { ["radius":"Circle":private]=> int(4) } $circle =FactoryShape::create(4); var_dump($circle);
2022年08月02日
401 阅读
0 评论
0 点赞
2022-08-02
【设计模式】单例设计模式(Singleton)
所谓单例模式,即在应用程序中最多只有该类的一个实例存在,一旦创建,就会一直存在于内存中!应用场景: 单例设计模式常应用于数据库类设计,采用单例模式,只连接一次数据库,防止打开多个数据库连接。一个单例类应具备以下特点: 单例类不能直接实例化创建,而是只能由类本身实例化。因此,要获得这样的限制效果,构造函数必须标记为private,从而防止类被实例化。需要一个私有静态成员变量来保存类实例和公开一个能访问到实例的公开静态方法。在PHP中,为了防止他人对单例类实例克隆,通常还为其提供一个空的私有__clone()方法。单例模式的例子:<?php /** * Singleton of Database */ class Database { // We need a static private variable to store a Database instance. privatestatic $instance; // Mark as private to prevent it from being instanced. private function__construct() { // Do nothing. } private function__clone() { // Do nothing. } public static function getInstance() { if (!(self::$instance instanceof self)) { self::$instance = new self(); } return self::$instance; } } $a =Database::getInstance(); $b =Database::getInstance(); // true var_dump($a === $b);
2022年08月02日
340 阅读
0 评论
0 点赞
2022-08-02
【设计模式】设计模式六大原则
设计模式六大原则开放封闭原则 :一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。里氏替换原则 :所有引用基类的地方必须能透明地使用其子类的对象.依赖倒置原则 :高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。单一职责原则 :不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。接口隔离原则 :客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。迪米特法则 :一个对象应该对其他对象保持最少的了解。
2022年08月02日
758 阅读
1 评论
0 点赞
2022-07-25
【Windows】host文件路径
C:\Windows\System32\drivers\etc
2022年07月25日
314 阅读
0 评论
0 点赞
2022-07-15
【网赚】支付宝扫码
暂无简介
2022年07月15日
332 阅读
0 评论
0 点赞
2022-07-14
【Linux】 cron 定时任务工具详解
一、cron 概述cron 是 Linux 系统下的定时执行工具,用于按预设时间自动运行脚本或命令。用户权限:除根用户外,普通用户也可通过 crontab 工具配置个人 cron 任务。配置文件存放:所有用户定义的 crontab 任务保存在 /var/spool/cron 目录中,以用户名命名(如 /var/spool/cron/username),并以创建者身份执行。工作机制:cron 守护进程每分钟会检查 /etc/crontab 文件、/etc/cron.d/ 目录及 /var/spool/cron 目录的变化,若有更新会自动载入内存,无需重启守护进程。二、crontab 安装与服务管理1. 安装 crontabyum install crontabs # 适用于 CentOS/RHEL 系统2. 服务控制命令/sbin/service crond start # 启动服务 /sbin/service crond stop # 关闭服务 /sbin/service crond restart # 重启服务 /sbin/service crond reload # 重新载入配置(修改任务后无需重启服务,执行此命令生效)3. 服务状态查看service crond status # 查看 crontab 服务运行状态4. 开机自启动配置# 设置 crond 服务在运行级别 3 和 5 下开机自启 chkconfig --level 35 crond on # 验证开机自启设置(可选) ntsysv # 打开服务配置界面,检查 crond 是否被勾选三、crontab 基本使用1. 编辑用户定时任务crontab -e # 编辑当前用户的 cron 任务(首次使用会提示选择编辑器,如 vim、nano)编辑完成后保存,任务会自动写入 /var/spool/cron/用户名 文件。2. 常用参数说明-e:编辑当前用户的定时任务-l:列出当前用户的所有定时任务-r:删除当前用户的所有定时任务-u <用户名>:指定用户(仅 root 可用),如 crontab -u john -e 编辑 john 的任务3. 执行 PHP 脚本的两种方式(1)直接执行本地 PHP 脚本示例:每小时执行一次 /home/john/myscript.phpcrontab -e # 编辑任务,添加以下内容 00 * * * * /usr/local/bin/php /home/john/myscript.php # 分钟 小时 日 月 星期 命令(2)通过 URL 触发(需依赖浏览器或工具)使用 lynx 文本浏览器(每小时访问 URL 执行脚本):00 * * * * lynx -dump http://www.example.com/myscript.php # -dump 输出到标准输出使用 curl(每 5 分钟访问 URL,输出到临时文件):*/5 * * * * /usr/bin/curl -o temp.txt http://www.example.com/myscript.php # -o 指定输出文件使用 wget(每 10 分钟访问 URL,安静模式输出到临时文件):*/10 * * * * /usr/bin/wget -q -O temp.txt http://www.example.com/myscript.php # -q 安静模式,-O 指定输出文件四、crontab 任务格式详解基本格式分钟 小时 日 月 星期 命令 * * * * * *字段含义分钟:1~59(* 表示每分钟,*/n 表示每 n 分钟)小时:1~23(0 表示 0 点)日:1~31月:1~12星期:0~6(0 表示星期天)命令:要执行的脚本或命令(需写绝对路径,如 /usr/local/bin/php)特殊符号含义*:匹配范围内的所有值(如小时字段为 * 表示每小时)/:表示“每”(如分钟字段 */10 表示每 10 分钟)-:表示范围(如小时字段 18-23 表示 18 点到 23 点),:表示离散值(如日字段 1,10,22 表示 1 日、10 日、22 日)五、crontab 任务示例# 每晚 21:30 重启 lighttpd 30 21 * * * /usr/local/etc/rc.d/lighttpd restart # 每月 1、10、22 日的 4:45 重启 lighttpd 45 4 1,10,22 * * /usr/local/etc/rc.d/lighttpd restart # 每周六、周日的 1:10 重启 lighttpd 10 1 * * 6,0 /usr/local/etc/rc.d/lighttpd restart # 每天 18:00 至 23:00 之间,每隔 30 分钟重启 lighttpd 0,30 18-23 * * * /usr/local/etc/rc.d/lighttpd restart # 每星期六的 23:00 重启 lighttpd 0 23 * * 6 /usr/local/etc/rc.d/lighttpd restart # 每小时重启 lighttpd 0 */1 * * * /usr/local/etc/rc.d/lighttpd restart # 晚上 23 点到早上 7 点之间,每隔 1 小时重启 lighttpd 0 23-7/1 * * * /usr/local/etc/rc.d/lighttpd restart # 每月 4 号,及每周一到周三的 11:00 重启 lighttpd 0 11 4 * mon-wed /usr/local/etc/rc.d/lighttpd restart # 每年 1 月 1 日的 4:00 重启 lighttpd 0 4 1 jan * /usr/local/etc/rc.d/lighttpd restart通过以上内容,可掌握 cron 定时任务的安装、配置、格式及常见用法,灵活实现脚本的自动执行。
2022年07月14日
328 阅读
0 评论
0 点赞
2022-07-02
【composer】composer常用命令
全局配置(推荐) 所有项目都会使用该镜像地址:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/取消配置:composer config -g --unset repos.packagist若项目之前已通过其他源安装,则需要更新 composer.lock 文件,执行命令:composer update --lock1、composer list:获取帮助信息; 2、composer init:以交互方式填写composer.json文件信息; 3、composer install:从当前目录读取composer.json文件,处理依赖关系,并安装到vendor目录下; 4、composer update:获取依赖的最新版本,升级composer.lock文件; 5、composer require:添加新的依赖包到composer.json文件中并执行更新; composer remove twbs/bootstrap; 卸载依赖包 6、composer search:在当前项目中搜索依赖包; 7、composer show:列举所有可用的资源包; 8、composer validate:检测composer.json文件是否有效; 9、composer self-update:将composer工具更新到最新版本; composer self-update -r :回滚到安装的上一个版本 10、composer diagnose:执行诊断命令 11、composer clear:清除缓存 10、composer create-project:基于composer创建一个新的项目; 11、composer dump-autoload:在添加新的类和目录映射是更新autoloader 关于composer遇到的问题1.You may need to run composer update with the “–no-plugins” option.回滚到安装的上一个版本Failed to decode response: zlib_decode(): data error 更新失败提示:更换回原来的源2.composer config -g repo.packagist composer https://packagist.phpcomposer.com更新组件提示:升级自身版本composer self-update
2022年07月02日
1,357 阅读
0 评论
1 点赞
2022-06-29
【JavaScript】js接受后端变量处理数组 eval()
前端 var arr = eval("{$arr}"); 后端: $arr = json_encode($dtArr); 'arr' => str_replace('"','',$arr),
2022年06月29日
324 阅读
0 评论
0 点赞
2022-06-29
【PHP】PHP报错ssl3_get_server_certificate:certificate verify failed问题
getimagesize(): SSL operation failed with code 1. OpenSSL Error message:error14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed提示缺少证书,搜到很多帖子写的感觉不是很清楚,以下步骤我自己记录一下先下载cacert.pem证书, https://curl.se/ca/cacert.pem,下载完后我直接上传到了/www/server/php目录下找到对应的php版本的php.ini文件将openssl.cafile其路径替换为openssl.cafile=/www/server/php/cacert.pem也就是你刚把cacert.pem证书上传的路径最后重启php服务即可
2022年06月29日
338 阅读
0 评论
0 点赞
2022-06-28
【PHP】PHP获取开始时间结束时间方法总汇
<?php /** * @author yfl QQ554665488 * demo Time funtion */ //返回今天的开始时间和结束时间 function day_now() { $arr = [ mktime(0, 0, 0, date('m'), date('d'), date('Y')), mktime(23, 59, 59, date('m'), date('d'), date('Y')), ]; return $arr; } //返回昨天开始结束时间 改造上边的方法 function day_yesterday() { $yesterday = date('d') - 1; $arr = [ mktime(0, 0, 0, date('m'), $yesterday, date('Y')), mktime(23, 59, 59, date('m'), $yesterday, date('Y')), ]; return $arr; } //获取当前时间的本周开始结束时间 function week_now() { $arr = [ strtotime(date('Y-m-d', strtotime("-1 week Monday", time()))), strtotime(date('Y-m-d', strtotime("+0 week Sunday", time()))) - 1 ]; return $arr; } // var_dump(week_now()); // echo date('Y-m-d',strtotime('next Monday',time())); //返回上周开始和结束的时间戳 function last_week() { // 1520179200 1520783999 $arr = [ // date('Y-m-d',strtotime('last week Monday',time())), // date('Y-m-d',strtotime('last week Sunday',time())) strtotime('last week Monday', time()), strtotime('last week Sunday +1 days -1 seconds', time()) ]; return $arr; } // var_dump(last_week()); // 返回本月开始和结束的时间戳 function now_month() { $arr = [ mktime(0, 0, 0, date('m'), 1, date('Y')), mktime(23, 59, 59, date('m'), date('t'), date('Y')) ]; return $arr; } // var_dump(now_month()); // 返回某一年某一月的开始和结束的时间戳 function month_year($year, $month) { return [ $begin = mktime(0, 0, 0, $month, 1, $year), $end = mktime(23, 59, 59, $month, date('t', $begin), $year) ]; } // var_dump(month_year(2017,3)); // 返回当前季度的开始时间和结束时间 function now_quarter($month = 0) { $month = $month != 0 ? $month : date('n'); $season = ceil($month / 3); return [ mktime(0, 0, 0, ($season - 1) * 3 + 1, 1, date('Y')), mktime(0, 0, 0, $season * 3, date('t'), date('Y')) - 1 ]; } // var_dump(now_quarter()); // 返回上个月开始和结束的时间戳 function lastMonth() { $begin = mktime(0, 0, 0, date('m') - 1, 1, date('Y')); $end = mktime(23, 59, 59, date('m') - 1, date('t', $begin), date('Y')); return [$begin, $end]; } // var_dump(lastMonth());
2022年06月28日
324 阅读
0 评论
0 点赞
2022-06-27
【PHP】TP6.0验证码接口
安装验证码扩展:composer require topthink/think-captcha创建验证码接口: public function setYzm(){ $captcha_img = Captcha_src(); $src = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_NAME'].'/api'.captcha_src(); ob_clean(); //清除缓冲区,防止出现“图像因其本身有错无法显示'的问题 $type = getimagesize($src)['mime']; //获取图片类型 header("Content-Type:{$type}"); $imgData = file_get_contents($src); //获取图片二进制流 $base64String = 'data:' . $type . ';base64,' . base64_encode($imgData); return show(200,'请求成功',$base64String); } 可以不用base64处理直接返回验证码地址 public function checkYzm($yzm){ if(!Captcha::checkApi($yzm)) throw new Exception('验证码验证失败~'); return true; }修改底层配置路径:vendor/topthink/think-captcha/src/Captcha.php/** * 创建验证码 * @return array * @throws Exception */ protected function generate(): array { $bag = ''; if ($this->math) { $this->useZh = false; $this->length = 5; $x = random_int(10, 30); $y = random_int(1, 9); $bag = "{$x} + {$y} = "; $key = $x + $y; $key .= ''; } else { if ($this->useZh) { $characters = preg_split('/(?<!^)(?!$)/u', $this->zhSet); } else { $characters = str_split($this->codeSet); } for ($i = 0; $i < $this->length; $i++) { $bag .= $characters[rand(0, count($characters) - 1)]; } $key = mb_strtolower($bag, 'UTF-8'); } $hash = password_hash($key, PASSWORD_BCRYPT, ['cost' => 10]); $this->session->set('captcha', [ 'key' => $hash, ]); //加上这行代码,便于后续校验验证码 if(empty(cache($key))){ cache($key, $hash, 5*60); }else{ return $this->generate(); } //加上这行代码,便于后续校验验证码 return [ 'value' => $bag, 'key' => $hash, ]; } /** * 验证验证码是否正确 * @access public * @param string $code 用户验证码 * @return bool 用户验证码是否正确 */ public function checkApi(string $code): bool { if (!cache($code)) { return false; } $key = cache($code); $code = mb_strtolower($code, 'UTF-8'); $res = password_verify($code, $key); if ($res) { cache($code,NULL); } return $res; }
2022年06月27日
401 阅读
0 评论
0 点赞
2022-06-27
【PHP】删除数组指定的键
/** * php除数组指定的key值(直接删除key值实现) * @param unknown $data * @param unknown $key * @return unknown */ function array_remove($data, $key){ if(!array_key_exists($key, $data)){ return $data; } $keys = array_keys($data); $index = array_search($key, $keys); if($index !== FALSE){ array_splice($data, $index, 1); } return $data; } /** * php除数组指定的key值(通过直接重新组装一个数组) * @param unknown $data * @param unknown $key * @return unknown */ function array_remove1($data,$delKey) { $newArray = array(); if(is_array($data)) { foreach($data as $key => $value) { if($key !== $delKey) { $newArray[$key] = $value; } } }else { $newArray = $data; } return $newArray; } $data = array('apple','address','ChinaGuangZhou'); $result = array_remove($data, 'name'); $result1 = array_remove1($data, 'name'); print_r($result); print_r($result1);
2022年06月27日
360 阅读
0 评论
0 点赞
2022-06-25
【PHP】PHP实现网络请求的方法及函数总结
一、分析php发送网网络请求的方法对于php发送网络请求,我们最常用的请求就是curl,有时我们也会用到file_get_contents函数发送网络请求,但file_get_contents只能完成一些间单的网络请求,稍复杂的就无法完成,例如文件上传,cookies,验证,表单提交等,用php的curl可以使用URL的语法模拟浏览器来传输数据,因为它是模拟浏览器,因此它同样支持多种协议,FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP等协议都可以很好的支持,包括一些:HTTPS认证,HTTP POST方法,HTTP PUT方法,FTP上传,keyberos认证,HTTP上传,代理服务器,cookies,用户名/密码认证,下载文件断点续传,上传文件断点续传,http代理服务器管道,甚至它还支持IPv6,scoket5代理服务器,通过http代理服务器上传文件到FTP服务器等等,所以我们在开发中尽量用curl发网络请求,无论是简单还是复杂二、file_get_contents发送网络请求示例file_get_contents(path,include_path,context,start,max_length)一般用file_get_contents或者fopen, file , readfile等函数读取url的时候 会创建一个$http_response_header变量保存HTTP响应的报头,使用fopen等函数打开的数据流信息可以用stream_get_meta_data获取$html = file_get_contents('http://www.baidu.com'); print_r($http_response_header); $fp = fopen('http://www.baidu.com', 'r'); print_r(stream_get_meta_data($fp)); fclose($fp);摸拟post请求:$url = 'http://192.168.1.1/test.php'; $data = array( 'keyword' => 'test data', ); $content = http_build_query($data); $content_length = strlen($content); $options = array( 'http' => array( 'method' => 'POST', 'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-length: $content_length\r\n", 'content' => $content ) ); echo file_get_contents($url, false, stream_context_create($options));三、php 用curl发送网络请求curl可以支持https认证、http post、ftp上传、代理、cookies、简单口令认证等等功能,使用前需要先在你的PHP环境中安装和启用curl模块,这里有两种写法供大家参考:<?php function geturl($url){ $headerArray =array("Content-type:application/json;","Accept:application/json"); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch,CURLOPT_HTTPHEADER,$headerArray); $output = curl_exec($ch); curl_close($ch); $output = json_decode($output,true); return $output; } function posturl($url,$data){ $data = json_encode($data); $headerArray =array("Content-type:application/json;charset='utf-8'","Accept:application/json"); $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,FALSE); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); curl_setopt($curl,CURLOPT_HTTPHEADER,$headerArray); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($curl); curl_close($curl); return json_decode($output,true); } function puturl($url,$data){ $data = json_encode($data); $ch = curl_init(); //初始化CURL句柄 curl_setopt($ch, CURLOPT_URL, $url); //设置请求的URL curl_setopt ($ch, CURLOPT_HTTPHEADER, array('Content-type:application/json')); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); //设为TRUE把curl_exec()结果转化为字串,而不是直接输出 curl_setopt($ch, CURLOPT_CUSTOMREQUEST,"PUT"); //设置请求方式 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);//设置提交的字符串 $output = curl_exec($ch); curl_close($ch); return json_decode($output,true); } function delurl($url,$data){ $data = json_encode($data); $ch = curl_init(); curl_setopt ($ch,CURLOPT_URL,$put_url); curl_setopt ($ch, CURLOPT_HTTPHEADER, array('Content-type:application/json')); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_setopt($ch, CURLOPT_POSTFIELDS,$data); $output = curl_exec($ch); curl_close($ch); $output = json_decode($output,true); } function patchurl($url,$data){ $data = json_encode($data); $ch = curl_init(); curl_setopt ($ch,CURLOPT_URL,$url); curl_setopt ($ch, CURLOPT_HTTPHEADER, array('Content-type:application/json')); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); curl_setopt($ch, CURLOPT_POSTFIELDS,$data); //20170611修改接口,用/id的方式传递,直接写在url中了 $output = curl_exec($ch); curl_close($ch); $output = json_decode($output); return $output; } ?>一个函数片时各种请求:function sendCurl($url, $data = null,$method='POST') { $method=strtoupper($method); $start_wdmcurl_time = microtime(true); $header = array(' application/x-www-form-urlencoded'); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FAILONERROR, false); // https 请求 if (strlen($url) > 5 && strtolower(substr($url, 0, 5)) == "https") { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); } if($method=='GET'){ if($data && is_array($data) && count($data)>0 ){ $url.="?".http_build_query($data); } curl_setopt($ch, CURLOPT_URL, $url); }elseif($method=='POST'){ curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); if (is_array($data) && count($data)>0) { curl_setopt($ch, CURLOPT_POST, true); $isPostMultipart = false; foreach ($data as $k => $v) { if ('@' == substr($v, 0, 1)) { $isPostMultipart = true; break; } } unset($k, $v); if ($isPostMultipart) { curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } else { curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); } } }elseif(in_array($method,['PUT','DELETE','PATCH'])){ curl_setopt($ch, CURLOPT_CUSTOMREQUEST,$method); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch,CURLOPT_HTTPHEADER,$header); $reponse = curl_exec($ch); curl_close($ch); return $reponse; }四、使用php composer的扩展guzzlehttpcomposer require guzzlehttp/guzzle $client = new \GuzzleHttp\Client(); $response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); echo $response->getStatusCode(); // 200 echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8' echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}' // Send an asynchronous request. $request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org'); $promise = $client->sendAsync($request)->then(function ($response) { echo 'I completed! ' . $response->getBody(); }); $promise->wait();日常开发中我们尽量用方法三,自定义用curl处理网络请求,或用composer的guzzlehttp扩展库,有起来也很方便。学习地址:https://mp.weixin.qq.com/s/MOQyelYTv_UYYx9dqIOVRw
2022年06月25日
317 阅读
0 评论
0 点赞
2022-06-23
【composer】composer 常用命令
1、composer安装 ------------ 官方地址:https://getcomposer.org/download/ 下载地址:https://getcomposer.org/Composer-Setup.exe 下载后直接安装即可。 2、检查是否安装完成 ---------- > composer --version > composer -V 注意这里要大写 > composer -vvv 命令查看更详细的信息,及帮助。 composer -V Composer version 2.0.9 2021-01-27 16:09:27 3、composer配置镜像 -------------- 因为composer是国外地址,访问起来特别慢,所以使用的时候很容易出现安装失败的情况,这里的解决办法就是使用镜像,使用composer config命令配置镜像地址即可。这里推荐使用阿里云composer镜像源,优点是快速稳定更新快 **3.1、全局配置(推荐)** 所有项目都会使用该镜像地址: composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 取消配置: composer config -g --unset repos.packagist **3.2、当前项目配置** 仅修改当前工程配置,仅当前工程可使用该镜像地址: composer config repo.packagist composer https://mirrors.aliyun.com/composer/ 取消配置: composer config --unset repos.packagist 4、composer使用 ------------ **4.1、install命令** > install命令可以用于项目初始化后,初次安装依赖,且会优先读取composer.lock中的版本号,以尽可能的保证协作开发中包版本的一致性。 composer install **4.2、require命令(不编辑composer.json的情况下安装库)** > 你可能会觉得每安装一个库都需要修改composer.json太麻烦,那么你可以直接使用require命令。 require > 命令,添加新的依赖包到composer.json文件中并执行更新。 composer require laravel/ui x.0.0 #下载指定版本,可指定 > 这个方法也可以用来快速地新开一个项目。init命令有–require选项,可以自动编写composer.json:(注意我们使用-n,这样就不用回答问题) $ composer init --require=foo/bar:1.0.0 -n $ cat composer.json { "require": { "foo/bar": "1.0.0" } } **4.3、update命令** > update命令无法在命令行中指定包版本号,需要手动修改composer.json文件 composer update 仅更新单个库 只想更新某个特定的库,不想更新它的所有依赖,很简单: composer update foo/bar **4.4、创建项目** > 初始化的时候,你试过create-project命令么? > 2.2.0 这会自动克隆仓库,并检出指定的版本。克隆库的时候用这个命令很方便,不需要搜寻原始的URI了。 composer create-project doctrine/orm path 2.2.0 **4.5、其他常用命令** composer list:获取帮助信息; composer init:以交互方式填写composer.json文件信息; composer search:在当前项目中搜索依赖包; composer show:列举所有可用的资源包; composer show -t:树状列举所有可用的资源包; composer show laravel/framework:这将向您显示安装的版本、它的许可证和依赖项以及它在本地安装的位置等信息。 composer outdated: 检测一下已安装的包,哪些有可以升级的; composer outdated -m:如果希望高亮显示小的升级版本,可以使用 outdated 命令,以 --minor-only 或者 -m 参数 ; composer why vlucas/phpdotenv:如果您想知道安装特定软件包的原因,可以使用 why 命令来确定哪些依赖项需要它; composer why-not laravel/framework 5.8 -t:有时,一个或多个已安装的软件包将阻止安装或更新软件包。 ; composer validate:检测composer.json文件是否有效; composer create-project:基于composer创建一个新的项目; composer dump-autoload:在添加新的类和目录映射是更新autoloader composer remove laravel/ui :删除依赖后还需要去vender文件夹里,手动删除 composer status -v:您可以使用 --verbose 或 -v 参数来查看本地修改的软件包和文件 composer licenses:用于查询许可的完整列表: 5、奇淫巧技 ------ 5.1、考虑缓存,dist包优先 > 最近一年以来的Composer会自动存档你下载的dist包。默认设置下,dist包用于加了tag的版本, > 例如"symfony/symfony":“v2.1.4”,或者是通配符或版本区间,“2.1.*“或”>=2.2,<2.3-dev”(如果你使用stable作为你的minimum-stability)。 > dist包也可以用于诸如dev-master之类的分支,Github允许你下载某个git引用的压缩包。 > 为了强制使用压缩包,而不是克隆源代码,你可以使用install和update的–prefer-dist选项 下面是一个例子(我使用了–profile选项来显示执行时间): $ composer init --require="twig/twig:1.*" -n --profile Memory usage: 3.94MB (peak: 4.08MB), time: 0s $ composer install --profile Loading composer repositories with package information Installing dependencies - Installing twig/twig (v1.12.2) Downloading: 100% Writing lock file Generating autoload files Memory usage: 10.13MB (peak: 12.65MB), time: 4.71s $ rm -rf vendor $ composer install --profile Loading composer repositories with package information Installing dependencies from lock file - Installing twig/twig (v1.12.2) Loading from cache Generating autoload files Memory usage: 4.96MB (peak: 5.57MB), time: 0.45s 这里,twig/twig:1.12.2的压缩包被保存在~/.composer/cache/files/twig/twig/1.12.2.0-v1.12.2.zip。重新安装包时直接使用。 **5.2、若要修改,源代码优先** > 当你需要修改库的时候,克隆源代码就比下载包方便了。你可以使用–prefer-source来强制选择克隆源代码。 composer update symfony/yaml --prefer-source 接下来你可以修改文件: composer status -v You have changes in the following dependencies: /path/to/app/vendor/symfony/yaml/Symfony/Component/Yaml: M Dumper.php 当你试图更新一个修改过的库的时候,Composer会提醒你,询问是否放弃修改: $ composer update Loading composer repositories with package information Updating dependencies - Updating symfony/symfony v2.2.0 (v2.2.0- => v2.2.0) The package has modified files: M Dumper.php Discard changes [y,n,v,s,?]? **5.3、为生产环境作准备** 最后提醒一下,在部署代码到生产环境的时候,别忘了优化一下自动加载: composer dump-autoload --optimize 安装包的时候可以同样使用–optimize-autoloader。不加这一选项,你可能会发现20%到25%的性能损失。 6、其它 ---- 参考文档:https://docs.phpcomposer.com/03-cli.html 参考链接:https://blog.csdn.net/jugtba/article/details/117379062
2022年06月23日
427 阅读
0 评论
0 点赞
2022-06-23
【PHP】ThinkPHP6 公共 上传到本地的方法 附带使用方法
<?php namespace app\common\lib\files; use think\Exception; use think\exception\ValidateException; class upload { private $domain; protected $name; protected $type; protected $module; protected $image; public function __construct($name = '',$type = 'image',$image = []) { $this->name = $name; $this->type = $this->checkUpload($type); $this->module = request()->app_name(); $this->image = $image; $this->domain = Request()->domain(); } protected $config = [ 'image' => [ 'validate' => [ 'size' => 10*1024*1024, 'ext' => 'jpg,png,gif,jpeg', ], 'path' => '/images', ], 'audio' => [ 'validate' => [ 'size' => 100*1024*1024, 'ext' => 'mp3,wav,cd,ogg,wma,asf,rm,real,ape,midi', ], 'path' => '/audios', ], 'video' => [ 'validate' => [ 'size' => 100*1024*1024, 'ext' => 'mp4,avi,rmvb,rm,mpg,mpeg,wmv,mkv,flv', ], 'path' => '/videos', ], 'file' => [ 'validate' => [ 'size' => 5*1024*1024, 'ext' => 'doc,docx,xls,xlsx,pdf,ppt,txt,rar,zip,pem,p12', ], 'path' => '/files', ], ]; private function checkUpload($type){ try{ if(empty($_FILES) || empty($_FILES[$this->name])) throw new Exception("未上传文件!"); if(!in_array($type,array_keys($this->config))) throw new Exception("文件类型不存在!"); return $type; }catch (Exception $e){ return \app\common\controller\Base::show(100,$e->getMessage()); } } public function upfile($infoSwitch = false){ $file = request()->file($this->name); try{ if($file == null) throw new ValidateException("the file cannot be empty"); validate(['file' => self::validateFile()])->check(['file' => $file]); $savename = \think\facade\Filesystem::disk('public')->putFile( $this->module.$this->config[$this->type]['path'], $file); if($infoSwitch){ return self::getFileInfo($file,$savename); } return $savename; }catch (\think\exception\ValidateException $e){ return \app\common\controller\Base::show(100,self::languageChange($e->getMessage())); } } private function validateFile(){ if(empty($this->image)){ $validataType = [ 'fileSize' => $this->config[$this->type]['validate']['size'], 'fileExt' => $this->config[$this->type]['validate']['ext'], ]; }else{ if(is_array($this->image)) throw new ValidateException(""); $validataType = [ 'fileSize' => $this->config[$this->type]['validate']['size'], 'fileExt' => $this->config[$this->type]['validate']['ext'], 'image' => $this->image //示例值 [200,200] ]; } return $validataType; } private function languageChange($msg){ $data = [ 'the file cannot be empty' => '文件不能为空!', 'unknown upload error' => '未知上传错误!', 'file write error' => '文件写入失败!', 'upload temp dir not found' => '找不到临时文件夹!', 'no file to uploaded' => '没有文件被上传!', 'only the portion of file is uploaded' => '文件只有部分被上传!', 'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!', 'upload write error' => '文件上传保存错误!', ]; return $data[$msg] ?? $msg; } private function getFileInfo($file,$savename){ $info = [ 'path' => config('filesystem.disks.public.url').'/'.str_replace('\\','/',$savename), 'url' => $this->domain.config('filesystem.disks.public.url').'/'.str_replace('\\','/',$savename), 'size' => $file->getSize(), 'name' => $file->getOriginalName(), 'mime' => $file->getMime(), 'ext' => $file->extension() ]; return $info; } } > 使用方法 : > > $instanceUpload = new upload($fileName,$fileType); > $info = $instanceUpload->upfile(true);
2022年06月23日
253 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP快速读取大文件指定行的方法
1、面临问题分析读取普通小文件我们一般用fopen 或者 file_get_contents就很方便简单 ,前者可以循环读取,后者可以一次性读取,但都是将文件内容一次性加载来操作。如果加载的文件特别大时,如几百M、上G时,这时性能贫瘠就非常突出了,那么PHP里有没有对大文件的处理函数或者类呢? 答案是:有的。2、SplFileObject类高效解决大文件读取问题从 PHP 5.1.0 开始,SPL 库增加了 SplFileObject 与 SplFileInfo 两个标准的文件操作类。从字面意思理解看,可以看出 SplFileObject 要比 SplFileInfo 更为强大。不错,SplFileInfo 仅用于获取文件的一些属性信息,如文件大小、文件访问时间、文件修改时间、后缀名等值,而 SplFileObject 是继承 SplFileInfo 这些功能并新增很多文件处理类操作方法的一个文件操作类。 /** 返回文件从X行到Y行的内容(支持php5、php4) * @param string $filename 文件名 * @param int $startLine 开始的行数 * @param int $endLine 结束的行数 * @return string */ function getFileLines($filename, $startLine = 1, $endLine=50, $method='rb') { $content = array(); $count = $endLine - $startLine; // 判断php版本(因为要用到SplFileObject,PHP>=5.1.0) if(version_compare(PHP_VERSION, '5.1.0', '>=')){ $fp = new SplFileObject($filename, $method); $fp->seek($startLine-1);// 转到第N行, seek方法参数从0开始计数 for($i = 0; $i <= $count; ++$i) { $content[]=$fp->current();// current()获取当前行内容 $fp->next();// 下一行 } }else{//PHP<5.1 $fp = fopen($filename, $method); if(!$fp) return 'error:can not read file'; for ($i=1;$i<$startLine;++$i) {// 跳过前$startLine行 fgets($fp); } for($i;$i<=$endLine;++$i){ $content[]=fgets($fp);// 读取文件行内容 } fclose($fp); } return array_filter($content); // array_filter过滤:false,null,'' }Ps:(1)、上面都没加”读取到末尾的判断”:!$fp->eof() 或者 !feof($fp),结果实践加上这个判断影响效率,而且这里加上也完全没必要。(2)、从上面的函数和实践操作就可以看出来使用SplFileObject类比下面的fgets函数效率要高很多,特别是文件行数非常多、并且要取越后面的内容的时候。fgets要两个循环才可以。
2022年06月23日
1,107 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP读取文件指定行的内容
function getLine($file, $line, $length = 40960){ $returnTxt = null; // 初始化返回 $i = 1; // 行数 $handle = @fopen($file, "r"); if ($handle) { while (!feof($handle)) { $buffer = fgets($handle, $length); if($line == $i) $returnTxt = $buffer; $i++; } fclose($handle); } return $returnTxt; }
2022年06月23日
295 阅读
0 评论
0 点赞
2022-06-23
【PHP】ThinkPHP 5.1公共上传类
<?php namespace app\extra; /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ //适配移动设备图片上传 use think\Exception; use think\facade\Request; class ExtraUpload{ /** * 默认上传配置 * @var array */ private $config = [ 'image' => [ 'validate' => [ 'size' => 10*1024*1024, 'ext' => 'jpg,png,gif,jpeg', ], 'rootPath' => './Uploads/images/', //保存根路径 ], 'audio' => [ 'validate' => [ 'size' => 100*1024*1024, 'ext' => 'mp3,wav,cd,ogg,wma,asf,rm,real,ape,midi', ], 'rootPath' => './Uploads/audios/', //保存根路径 ], 'video' => [ 'validate' => [ 'size' => 100*1024*1024, 'ext' => 'mp4,avi,rmvb,rm,mpg,mpeg,wmv,mkv,flv', ], 'rootPath' => './Uploads/videos/', //保存根路径 ], 'file' => [ 'validate' => [ 'size' => 5*1024*1024, 'ext' => 'doc,docx,xls,xlsx,pdf,ppt,txt,rar', ], 'rootPath' => './Uploads/files/', //保存根路径 ], ]; private $domain; function __construct() { //获取当前域名 $this->domain = Request::instance()->domain(); } public function upload($fileName){ if(empty($_FILES) || empty($_FILES[$fileName])){ // return ''; returnResponse(100,'文件为空'); } try{ $file = request()->file($fileName); if (is_array($file)){ $path = []; foreach ($file as $item){ $path[] = $this->save($item); } } else { $path = $this->save($file); } return $path; } catch (\Exception $e){ $arr = [ 'status' => 0, 'message' => $e->getMessage(), ]; header('Content-Type: application/json; charset=UTF-8'); exit(json_encode($arr)); } } public function uploadDetail($fileName){ if(empty($_FILES) || empty($_FILES[$fileName])){ // return []; returnResponse(100,'文件为空'); } try{ $file = request()->file($fileName); if (is_array($file)){ $path = []; foreach ($file as $item){ $detail = $item->getInfo(); $returnData['name'] = $detail['name']; $returnData['type'] = $detail['type']; $returnData['size'] = $detail['size']; $returnData['filePath'] = $this->save($item); $returnData['fullPath'] = $this->domain.$returnData['filePath']; $path[] = $returnData; } } else { $detail = $file->getInfo(); $returnData['name'] = $detail['name']; $returnData['type'] = $detail['type']; $returnData['size'] = $detail['size']; $returnData['filePath'] = $this->save($file); $returnData['fullPath'] = $this->domain.$returnData['filePath']; $path = $returnData; } return $path; } catch (\Exception $e){ $arr = [ 'status' => 0, 'message' => $e->getMessage(), ]; header('Content-Type: application/json; charset=UTF-8'); exit(json_encode($arr)); } } private function getConfig($file){ $name = pathinfo($file['name']); $end = $name['extension']; foreach ($this->config as $key=>$item){ if ($item['validate']['ext'] && strpos($item['validate']['ext'], $end) !== false){ return $this->config[$key]; } } return null; } private function save(&$file){ $config = $this->getConfig($file->getInfo()); if (empty($config)){ throw new Exception('上传文件类型不被允许!'); } // 移动到框架应用根目录/uploads/ 目录下 if ($config['validate']) { $file->validate($config['validate']); $result = $file->move($config['rootPath']); } else { $result = $file->move($config['rootPath']); } if($result){ $path = $config['rootPath']; if (strstr($path,'.') !== false){ $path = str_replace('.', '', $path); } return $path.$result->getSaveName(); }else{ // 上传失败获取错误信息 throw new Exception($file->getError()); } } } 使用方法: $p = new \app\extra\ExtraUpload(); return $p->uploadDetail('file');
2022年06月23日
374 阅读
0 评论
0 点赞
2022-06-23
【PHP】opcache是用来干嘛的?
opcache从字面意思,肯定是缓存这一块的。但是你是否知道它的工作原理是怎样的呢?这里一点一点让你了解!PHP项目中,尤其是在高并发大流量的场景中,如何提升PHP的响应时间,是一项十分重要的工作。而Opcache又是优化PHP性能不可缺失的组件,尤其是应用了PHP框架的项目中,作用更是明显。概述在理解 OPCache 功能之前,我们有必要先理解PHP-FPM + Nginx 的工作机制,以及PHP脚本解释执行的机制。1.1 PHP-FPM + Nginx 的工作机制请求从Web浏览器到Nginx,再到PHP处理完成,一共要经历如下五个步骤:第一步:启动服务启动PHP-FPM。PHP-FPM 支持两种通信模式:TCP socket和Unix socket;PHP-FPM 会启动两种类型的进程:Master 进程 和 Worker 进程,前者负责监控端口、分配任务、管理Worker进程;后者就是PHP的cgi程序,负责解释编译执行PHP脚本。启动Nginx。首先会载入 ngx_http_fastcgi_module 模块,初始化FastCGI执行环境,实现FastCGI协议请求代理这里要注意:fastcgi的worker进程(cgi进程),是由PHP-FPM来管理,不是Nginx。Nginx只是代理第二步:Request => NginxNginx 接收请求,并基于location配置,选择一个合适handler这里就是代理PHP的 handler第三步:Nginx => PHP-FPMNginx 把请求翻译成fastcgi请求通过TCP socket/Unix Socket 发送给PHP-FPM 的master进程第四步:PHP-FPM Master => WorkerPHP-FPM master 进程接收到请求分配Worker进程执行PHP脚本,如果没有空闲的Worker,返回502错误Worker(php-cgi)进程执行PHP脚本,如果超时,返回504错误处理结束,返回结果第五步:PHP-FPM Worker => Master => NginxPHP-FPM Worker 进程返回处理结果,并关闭连接,等待下一个请求PHP-FPM Master 进程通过Socket 返回处理结果Nginx Handler顺序将每一个响应buffer发送给第一个filter → 第二个 → 以此类推 → 最终响应发送给客户端1.2 PHP脚本解释执行的机制了解了PHP + Nginx 整体的处理流程后,我们接下来看一下PHP脚本具体执行流程,首先我们看一个实例: <?php if (!empty($_POST)) { echo "Response Body POST: ", json_encode($_POST), "\n"; } if (!empty($_GET)) { echo "Response Body GET: ", json_encode($_GET), "\n"; }我们分析一下执行过程:1.php初始化执行环节,启动Zend引擎,加载注册的扩展模块2.初始化后读取脚本文件,Zend引擎对脚本文件进行词法分析(lex),语法分析(bison),生成语法树3.Zend 引擎编译语法树,生成opcode,4.Zend 引擎执行opcode,返回执行结果在PHP cli模式下,每次执行PHP脚本,四个步骤都会依次执行一遍;在PHP-FPM模式下,步骤1)在PHP-FPM启动时执行一次,后续的请求中不再执行;步骤2)~4)每个请求都要执行一遍;其实步骤2)、3)生成的语法树和opcode,同一个PHP脚本每次运行的结果都是一样的,在PHP-FPM模式下,每次请求都要处理一遍,是对系统资源极大的浪费,那么有没有办法优化呢?当然有,如:OPCache:前身是Zend Optimizer+ ,是 Zend Server 的一个开源组件;官方出品,强力推荐APC:Alternative PHP Cache 是一个开放自由的 PHP opcode 缓存组件,用于缓存、优化 PHP 中间代码;已经不更新了不推荐APCu:是APC的一个分支,共享内存,缓存用户数据,不能缓存opcode,可以配合Opcache 使用eAccelerate:同样是不更新了,不推荐xCache:不再推荐使用了OPCache 介绍OPCache 是Zend官方出品的,开放自由的 opcode 缓存扩展,还具有代码优化功能,省去了每次加载和解析 PHP 脚本的开销。PHP 5.5.0 及后续版本中已经绑定了 OPcache 扩展。缓存两类内容:OPCodeInterned String,如注释、变量名等OPCache 原理OPCache缓存的机制主要是:将编译好的操作码放入共享内存,提供给其他进程访问。这里就涉及到内存共享机制,另外所有内存资源操作都有锁的问题,我们一一解读。3.1 共享内存UNIX/Linux 系统提供很多种进程间内存共享的方式:1.System-V shm API: System V共享内存,sysv shm是持久化的,除非被一个进程明确的删除,否则它始终存在于内存里,直到系统关机;2.mmap API:mmap映射的内存在不是持久化的,如果进程关闭,映射随即失效,除非事先已经映射到了一个文件上内存映射机制mmap是POSIX标准的系统调用,有匿名映射和文件映射两种mmap的一大优点是把文件映射到进程的地址空间避免了数据从用户缓冲区到内核page cache缓冲区的复制过程;当然还有一个优点就是不需要频繁的read/write系统调用3.POSIX API:System V 的共享内存是过时的, POSIX共享内存提供了使用更简单、设计更合理的API.4.Unix socket APIOPCache 使用了前三个共享内存机制,根据配置或者默认mmap 内存共享模式。依据PHP字节码缓存的场景,OPCache的内存管理设计非常简单,快速读写,不释放内存,过期数据置为Wasted。当Wasted内存大于设定值时,自动重启OPCache机制,清空并重新生成缓存。3.2 互斥锁任何内存资源的操作,都涉及到锁的机制。共享内存:一个单位时间内,只允许一个进程执行写操作,允许多个进程执行读操作;写操作同时,不阻止读操作,以至于很少有锁死的情况。这就引发另外一个问题:新代码、大流量场景,进程排队执行缓存opcode操作;重复写入,导致资源浪费。4. OPCache 缓存解读OPCache 是官方的Opcode 缓存解决方案,在PHP5.5版本之后,已经打包到PHP源码中一起发布。它将PHP编译产生的字节码以及数据缓存到共享内存中, 在每次请求,从缓存中直接读取编译后的opcode,进行执行。通过节省脚本的编译过程,提高PHP的运行效率。如果正在使用APC扩展,做同样的工作,现在强烈推荐OPCache来代替,尤其是PHP7中。4.1 OPCode 缓存Opcache 会缓存OPCode以及如下内容:PHP脚本涉及到的函数PHP脚本中定义的ClassPHP脚本文件路径PHP脚本OPArrayPHP脚本自身结构/内容4.2 Interned String 缓存首先我们需要理解,什么是 Interned String?在PHP5.4的时候, 引入了Interned String机制, 用于优化PHP对字符串的存储和处理。尤其是处理大块的字符串,比如PHP doces时,Interned String 可以优化内存。Interned String 缓存的内容包括:变量名称、类名、方法名、字符串、注释等。在PHP-FPM模式中,Interned String 缓存字符,仅限于Worker 进程内部。而缓存到OPCache中,那么Worker进程之间可以使用 Interned String 缓存的字符串,节省内存。我们需要注意一个事情,在PHP开发中,一般会有大段的注释,也会被缓存到OPCache中。可以通过php.ini的配置,关闭注释的缓存。但是,像Zend Framework等框架中,会引用注释,所以,是否关闭注释的缓存,需要区别对待。OPCache 更新策略是缓存,都存在过期,以及更新策略等。而OPCache的更新策略非常简单,到期数据置为Wasted,达到设定值,清空缓存,重建缓存。这里需要注意:在高流量的场景下,重建缓存是一件非常耗费资源的事儿。OPCache 在创建缓存时并不会阻止其他进程读取。这会导致大量进程反复新建缓存。所以,不要设置OPCache过期时间每次发布新代码时,都会出现反复新建缓存的情况。如何避免呢?不要在高峰期发布代码,这是任何情况下都要遵守的规则代码预热,比如使用脚本批量调PHP 访问URL,或者使用OPCache 暴露的API 如opcache_compile_file() 进行编译缓存OPCache 的配置6.1 内存配置opcache.preferred_memory_model="mmap" OPcache 首选的内存模块。如果留空,OPcache 会选择适用的模块, 通常情况下,自动选择就可以满足需求。可选值包括:mmap,shm, posix 以及 win32。opcache.memory_consumption=64 OPcache 的共享内存大小,以兆字节为单位,默认64Mopcache.interned_strings_buffer=4 用来存储临时字符串的内存大小,以兆字节为单位,默认4Mopcache.max_wasted_percentage=5 浪费内存的上限,以百分比计。如果达到此上限,那么 OPcache 将产生重新启动续发事件。默认56.2 允许缓存的文件数量以及大小opcache.max_accelerated_files=2000 OPcache 哈希表中可存储的脚本文件数量上限。真实的取值是在质数集合 { 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987 } 中找到的第一个大于等于设置值的质数。设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。默认值2000opcache.max_file_size=0 以字节为单位的缓存的文件大小上限。设置为 0 表示缓存全部文件。默认值06.3 注释相关的缓存opcache.load_commentsboolean 如果禁用,则即使文件中包含注释,也不会加载这些注释内容。本选项可以和 opcache.save_comments 一起使用,以实现按需加载注释内容。opcache.fast_shutdown boolean 如果启用,则会使用快速停止续发事件。所谓快速停止续发事件是指依赖 Zend 引擎的内存管理模块 一次释放全部请求变量的内存,而不是依次释放每一个已分配的内存块。6.4 二级缓存的配置opcache.file_cache 配置二级缓存目录并启用二级缓存。启用二级缓存可以在 SHM 内存满了、服务器重启或者重置 SHM 的时候提高性能。默认值为空字符串 "",表示禁用基于文件的缓存。opcache.file_cache_onlyboolean 启用或禁用在共享内存中的 opcode 缓存。opcache.file_cache_consistency_checksboolean 当从文件缓存中加载脚本的时候,是否对文件的校验和进行验证。opcache.file_cache_fallbackboolean 在 Windows 平台上,当一个进程无法附加到共享内存的时候, 使用基于文件的缓存,也即:opcache.file_cache_only=1。需要显示的启用文件缓存。
2022年06月23日
296 阅读
0 评论
0 点赞
1
...
4
5
6
...
13