PHP8新特性JIT
OOP的七大设计原则是什么?
-
开闭原则:对扩展开放,对修改关闭
-
里氏替换原则:继承 必须保证 父类中的性质在子类中仍然成立
-
依赖倒置原则:面向接口编程,而不面向实现类
-
单一职责原则:控制 类的 粒度的大小 ,增强内聚性,减少耦合
-
接口隔离原则:要为各个类提供所需的专用接口
-
迪米特法则:迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),
-
一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和
-
陌生人说话。英文简写为: LOD。
-
合成复用原则:尽可能使用组合或者聚合等关系来关联类,其次才考虑使用继承。
前五个合称 SOLID原则(单一职责原则、开放关闭原则、里氏替换原则、接口隔离原则和依赖倒置原则)
数组相加和array_merge
数组相加,如果第二个数组的某个键在第一个数组有相同的键,第二个数组中的键值对会被忽略,否则被追加在数组后面,例如
<?php
$arr1 = [4, 'apple' => 1, 'orange' => 2];
$arr2 = [3, 4, 'apple' => 2, 5, 6];
var_dump($arr1 + $arr2);
输出结果如下
array(6) {
[0]=>
int(4)
["apple"]=>
int(1)
["orange"]=>
int(2)
[1]=>
int(4)
[2]=>
int(5)
[3]=>
int(6)
}
其中第二个数组中键为0和apple的元素的值被抛弃,其他值被追加在后面。
array_merge 字符串键的值被后面的数组中相同键的值覆盖,索引键追加在后面
<?php
$arr1 = [4, 'apple' => 1, 'orange' => 2];
$arr2 = [3, 4, 'apple' => 2, 5, 6];
var_dump(array_merge($arr1, $arr2));
输出
array(7) {
[0]=>
int(4)
["apple"]=>
int(2)
["orange"]=>
int(2)
[1]=>
int(3)
[2]=>
int(4)
[3]=>
int(5)
[4]=>
int(6)
}
文件导出优化
PHP导出CSV
$fp = fopen('./badges.csv', 'w+');
fputs($fp, chr(239) . chr(187) . chr(191)); // utf-8 BOM头
fputcsv($fp, ['title', 'badge']);
代码示例
set_time_limit(0); // 设置不超时
header("Content-Type: application/octet-stream"); // 声明文件类型 以二进制流文件(可以是任何格式的文件)
// 根据浏览器类型 声明作为附件处理和下载后文件的名称
// Content-Disposition : 以什么方式下载 ; Content-Disposition:attachment :以附件的形式下载
// 例如如果下载的文件是txt 用户下载时保存时命名为 1.txt
if (preg_match("/MSIE/", $_SERVER['HTTP_USER_AGENT'])) {
header('Content-Disposition:attachment;filename="export.csv"');
} elseif (preg_match("/Firefox/", $_SERVER['HTTP_USER_AGENT'])) {
header('Content-Disposition:attachment;filename*="export.csv"');
} else {
header('Content-Disposition:attachment;filename="export.csv"');
}
// 用while(true) 和 sleep模拟分批查询,组装数据的过程
while (true) {
sleep(1);
echo 'hello<br>'; // 模拟数据输出
ob_flush();
flush();
}
常用header
header('HTTP/1.1 200 OK'); // ok 正常访问
header('HTTP/1.1 404 Not Found'); //通知浏览器 页面不存在
header('HTTP/1.1 301 Moved Permanently'); //设置地址被永久的重定向 301
header('Location: http://www.ithhc.cn/'); //跳转到一个新的地址
header('Refresh: 10; url=http://www.ithhc.cn/'); //延迟转向 也就是隔几秒跳转
header('X-Powered-By: PHP/6.0.0'); //修改 X-Powered-By信息
header('Content-language: en'); //文档语言
header('Content-Length: 1234'); //设置内容长度
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); //告诉浏览器最后一次修改时间
header('HTTP/1.1 304 Not Modified'); //告诉浏览器文档内容没有发生改变
###内容类型###
header('Content-Type: text/html; charset=utf-8'); //网页编码
header('Content-Type: text/plain'); //纯文本格式
header('Content-Type: image/jpeg'); //JPG、JPEG
header('Content-Type: application/zip'); // ZIP文件
header('Content-Type: application/pdf'); // PDF文件
header('Content-Type: audio/mpeg'); // 音频文件
header('Content-type: text/css'); //css文件
header('Content-type: text/javascript'); //js文件
header('Content-type: application/json'); //json
header('Content-type: application/pdf'); //pdf
header('Content-type: text/xml'); //xml
header('Content-Type: application/x-shockw**e-flash'); //Flash动画
######
###声明一个下载的文件###
header('Content-Type: application/octet-stream'); //声明输出的是二进制字节流
header('Accept-Ranges:bytes');//声明浏览器返回大小是按字节进行计算
header('Content-Disposition: attachment; filename="ITblog.zip"');
//声明作为附件处理和下载后文件的名称//告诉浏览器文件的总大小
//告诉浏览器文件的总大小
$fileSize = filesize($filePath);//坑 filesize 如果超过2G 低版本php会返回负数
header('Content-Length:' . $fileSize); //注意是'Content-Length:' 非Accept-Length
header('Content-Transfer-Encoding: binary');
readfile('test.zip');
######
###对当前文档禁用缓存###
header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
######
###显示一个需要验证的登陆对话框###
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="Top Secret"');
######
###声明一个需要下载的xls文件###
header('Content-Disposition: attachment; filename=ithhc.xlsx');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Length: '.filesize('./test.xls'));
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
proc_open使用
main.php
<?php
$proc = proc_open('php sub.php', [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
], $pipes);
pcntl_async_signals(true); // 如果没有这句,那需要在while循环中调用pcntl_signal_dispatch() 函数
foreach ($pipes as $pipe) {
stream_set_blocking($pipe, false);
}
pcntl_signal(SIGINT, function () {
echo "Exit";
exit;
});
while (true) {
sleep(1);
echo stream_get_contents($pipes[1]) . "123";
fwrite($pipes[0], "World");
echo "Main\n";
}
sub.php
<?php
while (true) {
sleep(1);
echo "Hello";
echo fread(STDIN, 1024); # 每1秒向标准输出输出Hello
}
EventSource php简单实现
EventSource - Web API 接口参考 | MDN
-
EventStream 的每条消息使用字段 + : + 内容构成。若没有冒号:,则整段将被当成一个字段名;若冒号前没有消息类型,则这是一条注释,会被前端忽略。
-
EventStream 虽然是基于 HTTP,但是在前端使用EventSource接口时,和 WebSocket 一样,并不能自定义 HTTP 请求头。鉴权方面只能通过传输同源下的 Cookie。并不能自定义一个 Authorization 头带 Token
-
同时,在不使用 HTTP/2 时,会受到浏览器的最大连接数限制。在 Chrome 和 Firefox 浏览器中所有打开选项卡下同域的连接最多只能有 6 个。而使用 HTTP/2 时,HTTP 同一时间内的最大连接数由服务器和客户端之间协商(默认为100)。https://developer.mozilla.org/zh-CN/docs/Server-sent_events/Using_server-sent_events#Event_stream_format
const event = new EventSource("http://localhost:8080/index.php")
event.addEventListener("message", function (event) {
console.log(event.data)
});
<?php
header('Content-Type: text/event-stream'); // 主要是这个头部
header("Cache-Control", "no-cache");
header("Connection", "keep-alive");
header("X-Accel-Buffering", "no"); // 这个主要是用于让nginx不要做缓冲
$i = 0;
while (true) {
sleep(1);
echo "id: 1\nretry: 3\nevent: message\ndata: " . $i++ . "\n\n";
ob_flush();
flush();
}
爬虫,HTML DOM操作
The DomCrawler Component (Symfony Docs)
安装
composer require symfony/dom-crawler
使用
替换img地址
$html = '<img src="https://codeemo.cn/images/logo.png">';
$crawler = new Crawler($html);
foreach ($crawler->filter('img') as $domElement) {
$domElement->hasAttribute('src');
$domElement->getAttribute('src');
$domElement->setAttribute('src', 'https://www.google.com');
}
echo $crawler->html();
这是基于PHP: DOM - Manual 的一个库。功能太强大,建议在大人陪同下使用。
yield 生成器,迭代器,聚合迭代器,协程
语法
$data = (yield $express);
yield 的左边是一个赋值语句,右边可以是值(也可是表达式) 。而yield 会先执行右边的表达式,并把值$value送到生成器外面。当生成器收到值后,会执行yield左边的语句,赋值给$data.
使用
<?php
$generator = (function () {
$a = yield 'first';
echo PHP_EOL;var_dump($a);
$b = yield 'second';
echo PHP_EOL;var_dump($b);
$c = yield;
echo PHP_EOL;var_dump($c);
})();
echo $generator->current();
$generator->next();
echo $generator->current();
$generator->next();
$generator->send('third');
php7之前需要(yield 123)
-
执行$generator->current(); 会执行第一个yield, 然后暂停
-
当执行$generator->next()或者$generator->send(null); 会往下执行直到第二个yield
-
当执行$generator->send('third'); 时候,yield实际是send的值, 将yield的值赋值给$a,所以代码中的$c为third,并且会执行下面的语句, 执行下一个yield,此时再调用$generator->current() 就是下一个yield
-
当生成器全部迭代完毕可以调用$generator->getReturn() 获取返回值
-
调用$generator->next()会到达下一个yield
-
第一次直接send() 会导致yield弹出值丢失
-
yield from 左边不能有接收,因为没有意义,但是可以嵌套
-
$c[] = yield 'name' => 'zhangsan' 支持这样的写法
-
当$gen迭代器被创建的时候一个rewind()方法已经被隐式调用,rewind执行会导致第一个yield被执行且忽略返回值
$g = function () {
yield 1;
yield 2;
};
$g = $g();
var_dump($g->send('test')); // 2
yield from 是一个强大且不可缺少的语法,如果只有 yield 那么就只是有了生成器,有了 yield from 那就有了一根强大的“针”——穿过一个个 生成器,按照call stack 把一个个生成器串了起来。 调用方法用 call_user_func(),调用 生成器用 yield from .
yield from: https://segmentfault.com/a/1190000022754223
协程
<?php
$logger = (function () {
$fd = fopen('./log.log', 'a+');
while (true) {
fwrite($fd, yield);
}
fclose($fd);
})();
$logger->send('test'. PHP_EOL);
ZH-CN : https://www.laruence.com/2015/05/28/3038.html EN-US: https://www.npopov.com/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html
中间件的实现
laravel pipeline
$pipes = [function (ArrayObject $passable, $next) {
$passable->offsetSet('name', 'zhangsan');
return $next($passable);
}];
$passable = new ArrayObject();
$res = array_reduce($pipes, function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
return $pipe($passable, $stack);
};
}, function ($passable) {
var_dump('then', $passable);
return 'I am response';
});
var_dump($res($passable));
psr实现(未实现接口,仅供参考)
$requestHandler = new class
{
protected $pipes;
protected $then;
public function __construct()
{
$this->pipes = [
new class
{
public function process(ArrayObject $request, $handler)
{
$request->offsetSet('name', 'zhangsan');
return $handler->handle($request);
}
},
new class
{
public function process(ArrayObject $request, $handler)
{
$request->offsetSet('gender', 'male');
return $handler->handle($request);
}
}
];
$this->then = function (ArrayObject $request) {
var_dump($request);
return 'I am response';
};
}
public function handle(ArrayObject $request)
{
if ($pipe = array_shift($this->pipes)) {
return $pipe->process($request, $this);
}
return ($this->then)($request);
}
};
$request = new ArrayObject();
$response = $requestHandler->handle($request);
var_dump($response);
ImagickPDF转图片
$im = new \Imagick();
$im->setResolution(120, 120);
$im->setCompressionQuality(100);
$im->readImage(public_path($originalLocalUrl));
foreach ($im as $key => $var) {
$var->setImageFormat('jpg');
$var->setImageCompressionQuality(0);
$width = $Var->getImageWidth();
$height = $Var->getImageHeight();
$newWidth = 600;
$radio = $newWidth / $width;
$newHeight = $radio * $height;
$var->resizeImage($newWidth, $newHeight, \Imagick::FILTER_LANCZOS, 1);
$filename = $key + 1 . '.jpg';
if ($var->writeImage(rtrim($realFilePath, '/') . '/' . $filename)) {
$pages[] = ltrim($filePath, '/') . '/' . $filename;
}
}
$im->clear();
$im->destroy();
下载
兼容 ios Safari 浏览器的下载头部
function download_headers($filename, $charset = 'UTF-8', $mimeType = 'application/octet-stream'): array
{
if (preg_match("/MSIE/", $_SERVER["HTTP_USER_AGENT"])) {
$filename = urlencode($filename);
$filename = str_replace("+", "%20", $filename);// 替换空格
$attachment = "attachment; filename=\"{$filename}\"; charset={$charset}";
} else if (preg_match("/Firefox/", $_SERVER["HTTP_USER_AGENT"])) {
$attachment = 'attachment; filename*=utf-8\'\'' . $filename;
} else if (preg_match("/Safari/", $_SERVER["HTTP_USER_AGENT"])) {
$filename = rawurlencode($filename); // 注意:rawurlencode与urlencode的区别
$attachment = 'attachment; filename*=utf-8\'\'' . $filename;
} else {
$attachment = "attachment; filename=\"{$filename}\"; charset={$charset}";
}
return [
'Cache-Control' => 'public, must-revalidate, max-age=0',
'Content-Type' => $mimeType,
'Content-Disposition' => $attachment,
];
}
XDebug + Phpstorm
php.ini 文件中添加如下配置
[xdebug]
zend_extension=<path-to-xdebug>
xdebug.mode = debug
xdebug.client_host = localhost
xdebug.idekey = "PHPSTORM"
xdebug.start_with_request= 1
Phpstorm Run/Debug Configurations 中添加 PHP Web Page ,添加Server ,host填127.0.0.1,port填8000,命令行使用php artisan serve启动服务监听8000端口,点击PhpStorm的Start Listen for PHP Debug Connections,点击debug按钮开始debug
也可以添加PHP script,然后使用php artisan serve就可以了