首页
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
Search
1
【PHP】PHPoffice/PHPSpreadsheet读取和写入Excel
1,673 阅读
2
【Layui】控制页面元素展示隐藏
1,520 阅读
3
【Git】No tracked branch configured for branch master or the branch doesn't exist.
1,460 阅读
4
【PHP】PHP实现JWT生成和验证
1,370 阅读
5
精准检测,助力社交管理 —— 微信好友检测服务来袭!
1,278 阅读
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
登录
Search
标签搜索
PHP
函数
方法
类
MySQL
ThinkPHP
JavaScript
OOP
Layui
Web
Server
Docker
Linux
PHPSpreadsheet
PHPoffice
Array
设计模式
Git
排序算法
基础
小破孩
累计撰写
244
篇文章
累计收到
13
条评论
首页
栏目
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
页面
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
搜索到
87
篇与
的结果
2023-03-23
【PHP】php.ini如何配置,才是PHP性能最大优化?
内存 默认设置memory_limit = 128M单个进程可使用的内存最大值,这个值的设定可以从以下几点考虑:应用的类型。如果是内存集中型应用,可增加该值;单个 PHP 进程平均消耗的内存,该值可通过多次运行同一个脚本来计算平均值;能负担多少个 php-fpm 进程;该值等于分配的总内存除以单个 PHP 进程平均消耗的内存文件上传 默认设置file_uploads = On max_file_uploads = 20 upload_max_filesize = 2M max_execution_time = 30 值 为 0 代表没有限制设置max_file_uploads来决定同时允许多少个文件上传;设置upload_max_filesize来决定每个文件上传的最大值;如果是长时间任务,尽量使用队列来处理,因此,可适当缩短max_execution_time的值;注意, Web 服务器也可以设置文件上传大小及超时时间,不能仅仅以 php.ini 的设置为准;会话 PHP 的会话默认是保存在硬盘中session.save_handler = files在实际应用中,应当将会话保存在内存中。可使用 Memcached 或者 Redis。这样做主要有两个好处:提高速度;有助于后期的扩展,如果会话数据存储在硬盘中,不便于增加额外的服务器,如果把会话数据存放在 Memcached 或 Redis 里,任何一台分布式 PHP-FPM 服务器都能访问会话数据。可通过 PECL 安装memcached扩展并设置默认的save_handler为memcachedsession.save_handler = 'memcached' session.save_path = '127.0.0.1:11211'缓冲输出 默认值realpath_cache_size = 4M realpath_cache_ttl = 120PHP 会缓存应用使用的文件路径,这样每次包含或导入文件时就无需不断搜索包含路径了,这个缓存叫真实路径缓存(realpath cache),如果运行的是大型的 PHP 文件(如 Composer 组件),使用了大量文件,增加 PHP 真实路径缓存的大小能得到更好的性能。
2023年03月23日
376 阅读
0 评论
0 点赞
2022-09-14
【PHP】PHP实现协同推荐算法
概述: 因为最近对算法这块进行了学习,所以最近对类似淘宝商品推荐的协同推荐算法进行了整理总结,本文将用php语言进行实现,文章将从以下几点进行终结: (1)什么是协同推荐算法?有什么用? (2)依据什么数学方法公式,为什么要用它? (3)实现流程是怎样的? (4)具体实现步骤。 首先,第一点,我们要实现一个算法必须要知道它有什么用,核心思想是什么?顾名思义,它的用途就是用来给我们做一些相似性推荐,推荐你喜欢的恭喜,根据你的好友等预测你喜欢的然后再推荐给你;我们举个例子来概括一下算法的核心思想:该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品 3.将a可能喜欢的物品推荐给a。 现在我们了解了这个核心思想以后,我们知道算法的核心是数学逻辑,我们必须理解其数学思想,然后再转化为编程思想达到我们的目标: 我们先回忆一下余弦定理: 可见cosA的夹角越小cosA的值越大,则边c和边b的长度越相近,因此我们利用余弦定理的向量方式类推出如下公式:1.余弦相似度(求邻居):2.预测公式(预测a可能会喜欢哪种物品): 接下来我们介绍一下实现流程: 现在我们具体看看怎样用php实现这个算法: (1)数据准备:在这里我们直接定义一组数据$userarray= [ ['name'=>'A','a'=>3,'b'=>2,'c'=>1,'d'=>5,'e'=>null,'f'=>null,'g'=>null], ['name'=>'B','a'=>1,'b'=>6,'c'=>6,'d'=>5,'e'=>2,'f'=>3,'g'=>5], ['name'=>'C','a'=>3,'b'=>5,'c'=>null,'d'=>4,'e'=>3,'f'=>3,'g'=>6], ['name'=>'D','a'=>4,'b'=>1,'c'=>1,'d'=>5,'e'=>3,'f'=>3,'g'=>3], ['name'=>'E','a'=>5,'b'=>1,'c'=>null,'d'=>4,'e'=>5,'f'=>1,'g'=>5], ['name'=>'F','a'=>1,'b'=>3,'c'=>2,'d'=>5,'e'=>6,'f'=>null,'g'=>4], ['name'=>'G','a'=>1,'b'=>5,'c'=>2,'d'=>5,'e'=>2,'f'=>3ll,'g'=>5] ]; (2)我们以A用户为对象分别计算出他们的余弦相似度,然后将其存入数组$cos中:/* * 以下示例只求A的推荐 */ $cos = []; $cos[0] = 0; $denominator_left = 0;//分母左边初始值 //开始计算cos //计算分母左边 for($i=1;$i<8;$i++){ if($userarray[0][$i] != null){//$userarray[0]代表A $denominator_left += $userarray[0][$i] * $userarray[0][$i]; } } $denominator_left = sqrt($denominator_left);//取算数平方根的值 for($i=1;$i<6;$i++){ $numerator = 0;//分子初始值 $denominator_right = 0;//分母右边初始值 for($j=1;$j<8;$j++){ //计算分子 if($userarray[0][$j] != null && $userarray[$i][$j] != null){ $numerator += $userarray[0][$j] * $userarray[$i][$j]; } //计算分母右边 if($userarray[$i][$j] != null){ $denominator_right += $userarray[$i][$j] * $userarray[$i][$j]; } } $denominator_right = sqrt($denominator_right ); $cos[$i]['cos'] = $numerator /$denominator_left /$denominator_right ;//存储当前用户的cos近似值 $cos[$i]['name'] = $userarray[$i]['name'];//存储当前用户的名字 $cos[$i]['e'] = $userarray[$i]['e'];//存储当前用户的e物品评分 $cos[$i]['f'] = $userarray[$i]['f'];//存储当前用户的f物品评分 $cos[$i]['g'] = $userarray[$i]['g'];//存储当前用户的g物品评分 }(3)我们对余弦近似值进行由大到小的排序,抽取前三个用户作为A用户的邻居,我们使用冒泡排序法对其进行排序// 第一层可以理解为从数组中键为0开始循环到最后一个 for ($i = 0; $i < count($cos) ; $i++) { // 第二层为从$i+1的地方循环到数组最后 for ($j = $i+1; $j < count($cos); $j++) { // 比较数组中两个相邻值的大小 if ($cos[$i]['cos'] < $cos[$j]['cos']) { $tem = $cos[$i]; // 这里临时变量,存贮$i的值 $cos[$i] = $cos[$j]; // 第一次更换位置 $cos[$j] = $tem; // 完成位置互换 } } } (4)接下来我们对A用户可能喜欢的物品进行预测,利用第二个公式,求出product值//计算A对e的评分 $numerator= 0;//分子初始值 $denominator= 0;//分母初始值 for($i=0;$i<3;$i++){ $numerator+= $cos[$i]['cos'] * $cos[$i]['e']; $denominator+= $cos[$i]['cos']; } $score_e = $numerator/sqrt($denominator); //计算A对f的评分 $numerator= 0;//分子初始值 $denominator= 0;//分母初始值 for($i=0;$i<3;$i++){ $numerator+= $cos[$i]['cos'] * $cos[$i]['f']; $denominator+= $cos[$i]['cos']; } $score_f= $numerator/sqrt($denominator); //计算A对g的评分 $numerator= 0;//分子初始值 $denominator= 0;//分母初始值 for($i=0;$i<3;$i++){ $numerator+= $cos[$i]['cos'] * $cos[$i]['g']; $denominator+= $cos[$i]['cos']; } $score_g= $numerator/sqrt($denominator);最后我们可以比较这些值,可以选取值最大的物品推荐给用户A
2022年09月14日
448 阅读
0 评论
1 点赞
2022-09-01
【PHP】PHP错误级别
在php编程过程中,大家一定会遇到或多或少的错误提醒,也正是这些错误提示,指引我们编写更加干净的代码,今天先写出我们主要列出的错误类型,先挖坑,写关于php错误与异常的相关知识,慢慢填坑。Deprecated最低级别错误,程序继续执行Notice 通知级别的错误 如直接使用未声明变量,程序继续执行 Warning 警告级别的错误,可能得不到想要的结果 Fatal error 致命级别错误致命级别错误,程序不往下执行 parse error 语法解析错误,最高级别错误,连其他错误信息也不呈现出来 E_USER_相关错误 用户设置的相关错误利用trigger_error()函数设置一个用户级别的 error/warning/notice 信息如何设置错误级别? error_reporting(-1)显示所有错误,error_reporting(0)屏蔽所有错误。ini_set('error_reporting',0)也是屏蔽所有错误。可以在php.ini文件中设置error_reporting来使脚本显示或不显示某些错误。ini_set('display_errors','On')显示错误。 注意:error_reporting()设置报告何种错误,而ini_set('display_errors','On')设置是否在输出错误。因而error_reporting(-1)和ini_set('display_errors',0)可用作设置日志:报告错误并且不输出。 举例:error_reporting(E_ALL&~E_NOTICE)不显示通知级别的错误。“~”表示非。
2022年09月01日
307 阅读
0 评论
0 点赞
2022-08-16
【PHP】PHPEmail的使用
第一步:使用composer安装phpmailercomposer require phpmailer/phpmailer第二步:common.php写个发送邮件的函数(腾讯邮箱的为例)/** * 系统邮件发送函数 * @param string $tomail 接收邮件者邮箱 * @param string $name 接收邮件者名称 * @param string $subject 邮件主题 * @param string $body 邮件内容 * @param string $attachment 附件列表 * @return boolean * @author static7 <static7@qq.com> */ function send_mail($tomail, $name, $subject = '', $body = '', $attachment = null) { $mail = new \PHPMailer(); //实例化PHPMailer对象 $mail->CharSet = 'UTF-8'; //设定邮件编码,默认ISO-8859-1,如果发中文此项必须设置,否则乱码 $mail->IsSMTP(); // 设定使用SMTP服务 $mail->SMTPDebug = 0; // SMTP调试功能 0=关闭 1 = 错误和消息 2 = 消息 $mail->SMTPAuth = true; // 启用 SMTP 验证功能 $mail->SMTPSecure = 'ssl'; // 使用安全协议 $mail->Host = "smtp.exmail.qq.com"; // SMTP 服务器 $mail->Port = 465; // SMTP服务器的端口号 $mail->Username = "static7@qq.com"; // SMTP服务器用户名 $mail->Password = ""; // SMTP服务器密码 $mail->SetFrom('static7@qq.com', 'static7'); $replyEmail = ''; //留空则为发件人EMAIL $replyName = ''; //回复名称(留空则为发件人名称) $mail->AddReplyTo($replyEmail, $replyName); $mail->Subject = $subject; $mail->MsgHTML($body); $mail->AddAddress($tomail, $name); if (is_array($attachment)) { // 添加附件 foreach ($attachment as $file) { is_file($file) && $mail->AddAttachment($file); } } return $mail->Send() ? true : $mail->ErrorInfo; }第三步:控制器方法里写发送的内容/** * tp5邮件 * @param * @author staitc7 <static7@qq.com> * @return mixed */ public function email() { $toemail='static7@qq.com'; $name='static7'; $subject='QQ邮件发送测试'; $content='恭喜你,邮件测试成功。'; dump(send_mail($toemail,$name,$subject,$content)); }第4步:测试发送请自行测试转发:https://www.thinkphp.cn/topic/44477.html
2022年08月16日
342 阅读
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日
318 阅读
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日
316 阅读
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日
351 阅读
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日
348 阅读
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日
305 阅读
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日
245 阅读
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,025 阅读
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日
280 阅读
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日
359 阅读
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日
270 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP实现批量修改图片名称
<?php // glob() 返回指定目录下的文件名以及目录 $arr = glob("blog.8688pic.com/*.JPG"); $i = 1; // 循环遍历 foreach($arr as $file) { // 获取图片后缀名 $ext = pathinfo($file,PATHINFO_EXTENSION); // 确定图片的新名字 $name = $i++ . "." . 'jpg'; // 重命名 rename($file, $name); } die(); ?>
2022年06月23日
243 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP随机生成字符串
public function setRandomString($len = 32) { $string = ''; $char = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; for ($i = 0; $i < $len; $i++) { $string .= $char[mt_rand(0, strlen($char) - 1)]; } return $string; }
2022年06月23日
255 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP反射
一、前言Reflection(反射)是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。这一特征在实际应用中也许用得不是很多。PHP从5.0开始完美支持反射API。PHP反射可以用于观察并修改程序在运行时的行为。一个面向反射的(reflection-oriented)程序组件可以监测一个范围内的代码执行情况,可以根据期望的目标与此相关的范围修改本身。PHP5具有完整的反射API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。二、概念反射是指在PHP运行状态中,扩展分析PHP程序,导出或提出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API。三、PHP反射的基本语法实现反射的方法有很多,可以通过实例化一个专门控制类的ReflectionClass类来实现反射,也可以在已有类实例的情况下,通过直接实例化ReflectionMethod类来执行反射方法,原理如图:以下是对反射类和反射方法类的基本用法:1、反射类(1) $reflectClass = new ReflectionClass(<类名>); 传入类名字符串,返回控制目标类的ReflectionClass类实例; (2) $reflectClass->getConstant(<常量名>); 传入类中定义了的常量名,返回常量值,可通过$reflectClass->getConstants返回类中所有定义的常量的数组; (3) $class = $reflectClass->newInstance(); 实例化类,返回目标类实例;也可通过$reflectClass->newInstanceArgs(<参数数组>)传入实例化的构造函数参数进行实例化; 2、反射方法(1) $reflectMethod = new ReflectionMethod(<方法名>); 传入方法名名字符串,返回控制目标方法的ReflectionMethod类实例; (2) $parameters = $reflectMethod->getParameters(); 获取该类所需的参数名,该方法返回一个包含所有参数名的二维数组; (3) $name = $parameters->getName(); 返回要执行的方法所需参数数组的单个参数名,可通过foreach循环逐一获取和赋值; (4) $reflectMethod->invokeArgs(<类实例>,<执行该方法所需参数数组>); 传入类实例和方法参数,执行方法,返回执行结果。 3、反射类和反射方法中其他常用的用法:ReflectionClass: ReflectionMethod: 4、除了ReflectionClass和ReflectionMethod,我们对于类中的参数、属性和php服务的环境变量、扩展等参数也是可以通过反射API的一些方法来执行的,如下:四、反射在实际应用中的使用1、反射可以用于文档、文件生成。可以用它对文件里的类进行扫描,逐个生成描述文档;2、既然反射可以探知类的内部结构,那么可以用它做hook实现插件功能;3、可以用于做动态代理,在未知或者不确定类名的情况下,动态生成和实例化一些类和执行方法;4、对于多次继承的类,我们可以通过多次反射探索到基类的结构,或者采用递归的形式反射,实现实例化所有继承类,这即是PHP依赖注入的原理。五、PHP反射的优缺点优点1、支持反射的语言提供了一些在低级语言中难以实现的运行时特性。2、可以在一定程度上避免硬编码,提供灵活性和通用性。3、可以作为一个第一类对象发现并修改源代码的结构(如代码块、类、方法、协议等)。4、可以在运行时像对待源代码语句一样计算符号语法的字符串(类似JavaScript的eval()函数),进而可将跟class或function匹配的字符串转换成class或function的调用或引用。5、可以创建一个新的语言字节码解释器来给编程结构一个新的意义或用途。缺点1、此技术的学习成本高。面向反射的编程需要较多的高级知识,包括框架、关系映射和对象交互,以利用更通用的代码执行。2、同样因为反射的概念和语法都比较抽象,过多地滥用反射技术会使得代码难以被其他人读懂,不利于合作与交流。3、由于将部分信息检查工作从编译期推迟到了运行期,此举在提高了代码灵活性的同时,牺牲了一点点运行效率。4、通过深入学习反射的特性和技巧,它的劣势可以尽量避免,但这需要许多时间和经验的积累。如何使用反射API? class person{ public $name; public $gender; public function say(){ echo $this->name," \tis ",$this->gender,"\r\n"; } public function set($name, $value) { echo "Setting $name to $value \r\n"; $this->$name= $value; } public function get($name) { if(!isset($this->$name)){ echo '未设置'; $this->$name="正在为你设置默认值"; } return $this->$name; } } $student=new person(); $student->name='Tom'; $student->gender='male'; $student->age=24; 现在,要获取这个student对象的方法和属性列表该怎么做呢?如以下代码所示: // 获取对象属性列表 $reflect = new ReflectionObject($student); $props = $reflect->getProperties(); foreach ($props as $prop) { print $prop->getName() ."\n"; } // 获取对象方法列表 $m=$reflect->getMethods(); foreach ($m as $prop) { print $prop->getName() ."\n"; } 也可以不用反射API,使用class函数,返回对象属性的关联数组以及更多的信息: // 返回对象属性的关联数组 var_dump(get_object_vars($student)); // 类属性 var_dump(get_class_vars(get_class($student))); // 返回由类的方法名组成的数组 var_dump(get_class_methods(get_class($student))); 假如这个对象是从其他页面传过来的,怎么知道它属于哪个类呢?一句代码就可以搞定: // 获取对象属性列表所属的类 echo get_class($student); 反射API的功能显然更强大,甚至能还原这个类的原型,包括方法的访问权限等,如: // 反射获取类的原型 $obj = new ReflectionClass('person'); $className = $obj->getName(); $Methods = $Properties = array(); foreach($obj->getProperties() as $v) { $Properties[$v->getName()] = $v; } foreach($obj->getMethods() as $v){ $Methods[$v->getName()] = $v; } echo "class {$className}\n{\n"; is_array($Properties)&&ksort($Properties); foreach($Properties as $k => $v) { echo "\t"; echo $v->isPublic() ? ' public' : '',$v->isPrivate() ? ' private' : '', $v->isProtected() ? ' protected' : '', $v->isStatic() ? ' static' : ''; echo "\t{$k}\n"; } echo "\n"; if(is_array($Methods)) ksort($Methods); foreach($Methods as $k => $v) { echo "\tfunction {$k}(){}\n"; } echo "}\n"; 输出如下: class person { public gender public name function get(){} function set(){} function say(){} } 不仅如此,PHP手册中关于反射API更是有几十个,可以说,反射完整地描述了一个类或者对象的原型。反射不仅可以用于类和对象,还可以用于函数、扩展模块、异常等。 反射的作用? ------ 反射可以用于文档生成。因此可以用它对文件里的类进行扫描,逐个生成描述文档。 既然反射可以探知类的内部结构,那么是不是可以用它做hook实现插件功能呢?或者是做动态代理呢? 例如: class mysql { function connect($db) { echo "连接到数据库${db[0]}\r\n"; } } class sqlproxy { private $target; function construct($tar) { $this->target[] = new $tar(); } function call($name, $args) { foreach ($this->target as $obj) { $r = new ReflectionClass($obj); if ($method = $r->getMethod($name)) { if ($method->isPublic() && !$method->isAbstract()) { echo "方法前拦截记录LOG\r\n"; $method->invoke($obj, $args); echo "方法后拦截\r\n"; } } } } } $obj = new sqlproxy('mysql'); $obj->connect('member'); 在平常开发中,用到反射的地方不多:一个是对对象进行调试,另一个是获取类的信息。在MVC和插件开发中,使用反射很常见,但是反射的消耗也很大,在可以找到替代方案的情况下,就不要滥用。 很多时候,善用反射能保持代码的优雅和简洁,但反射也会破坏类的封装性,因为反射可以使本不应该暴露的方法或属性被强制暴露了出来,这既是优点也是缺点。 <p>原文链接:https://blog.csdn.net/dream_successor/article/details/78287016</p>
2022年06月23日
896 阅读
0 评论
0 点赞
2022-06-23
【PHP】thinkphp5.1清除缓存 包括缓存日志 编译文件
/** * 清除缓存 */ public function clearCache(){ \think\facade\Cache::clear(); return ZHTReturn('清除成功',1); } /** * 清除模版缓存但不删除temp目录 */ public function clearTemp() { $path = env('RUNTIME_PATH'); // $path = env(); // dump($path); // die; array_map('unlink',glob($path.'temp\*.php')); return ZHTReturn('清除成功',1); } /** * 清除日志缓存并删出log空目录 */ public function clearLog() { $path = env('RUNTIME_PATH'); $path_log = glob($path.'log\*'); foreach ($path_log as $val) { array_map('unlink', glob($val . '\*.log')); rmdir($val); } return ZHTReturn('清除成功',1); } /** * 清除所有缓存 */ public function clearAll() { \think\facade\Cache::clear(); $path = env('RUNTIME_PATH'); array_map('unlink',glob($path.'temp\*.php')); $path_log = glob($path.'log\*'); foreach ($path_log as $val) { array_map('unlink', glob($val . '\*.log')); rmdir($val); } return ZHTReturn('清除成功',1); }
2022年06月23日
288 阅读
0 评论
0 点赞
2022-06-23
【PHP】获取字符串的两个字符中间的内容
/** * Author: 小破孩 * Email: 3584685883@qq.com * Time: 2022/2/14 16:12 * @param $str * @param $separator * @param string $mark * @return bool|false|mixed|string * Description:获取字符串 */ public function getSubStr($str,$separator,$mark=':'){ $arr = explode($separator,$str); if (empty($arr) || !isset($arr[1])) { return false; } $str = $arr[1]; if (strpos($str,$mark) !== false) { return substr($str,0,strpos($str,$mark)); }else{ return $str; } }
2022年06月23日
351 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP提取多维数组指定列的方法
$arr = array( '0' => array('id' => 1, 'name' => 'name1'), '1' => array('id' => 2, 'name' => 'name2'), '2' => array('id' => 3, 'name' => 'name3'), '3' => array('id' => 4, 'name' => 'name4'), '4' => array('id' => 5, 'name' => 'name5'), ); //需要得到的结果:$name_list = array('name1', 'name2', 'name3', 'name4', 'name5'); 1、使用array_column() PHP在5.5.0版本之后,添加了一个专用的函数array_column() 方法: $name_list = array_column($arr, 'name'); 2、array_walk()方法 array_walk()使用用户自定义函数对数组中的每个元素做回调处理 $name_list = array(); array_walk($arr, function($value, $key) use (&$name_list ){ $name_list [] = $value['name']; }); 3、array_map()方法 array_map()函数和array_walk() 作用类似,将回调函数作用到给定数组的单元上 $name_list = array(); array_map(function($value) use (&$name_list){ $name_list[] = $value['name']; }, $arr); 4、foreach循环遍历方法 foreach()循环相对上面的方法效率稍微低一些 $name_list = array(); foreach ($arr as $value) { $name_list[] = $value['name']; } 5、array_map变种 把$arr数组的每一项值的开头值移出,并获取移除的值作为新数组。注意此时新数组$name_list的键仍是原数组$arr的键 $name_list = array_map('array_shift', $arr); //注意:该功能会获取$arr中的 id 列,而不是name 列。 //另外,如果需要获取二维数组每一项的开头列或结尾列,也可以这样做: $name_list = array_map('reset', $arr); $name_list = array_map('end', $arr); //这三个变种方法作用比较局限,仅在获取第一列或最后一列的时候有用,在 //复杂的数组中就难以发挥作用了。
2022年06月23日
293 阅读
0 评论
0 点赞
1
2
3
...
5