[TOC]
搞PHP也5年了,算是有一些中型项目的开发经验,做个分享希望可以帮助到一些有点迷茫的同学,同时接受各方的批评和指点。
试试手,看看自己能hold住吗?
需要了解5.x版本每次升级主要新增了哪些特性,有利于我们写出更优雅的代码。
- Apache + mod_php
- Nginx + PHP-FPM
- OpenResty + PHP-FPM
PHP5.6升级PHP7的负担有哪些?
除了版本号之外, 还应该关注一下是否是线程安全
重IO/内存
phpqrcode在输出二维码可能出现显示问题,此时可以用ob_clean修正
一定要留意SORT_REGULAR
参数
Remove duplicate items from an array
存在平台兼容性问题,同时我们也要注意自定义请求头潜在的问题。
PHP在处理字符串时,会利用"!="或"=="来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
空字典json序列化成了[]
查询时大写的字段名称被转化成了小写
查询时整型的字段转化成了字符串
需要注意用PHP查询数据库得到的数据类型与数据库中存储的未必一致。PHP从MySQL取出int数据,变成了string,所以在编写API接口的时候,一定要添加如下设置:
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1);
php提供了高精度计算的函数库,实际上就是为了解决这个浮点数计算问题而生的。
SQL预处理能力提高了安全性以及执行效率。
执行效率高主要体现在预处理语句上,和常规编写的SQL最重要区别是,常规情况下每次都需要传递整条语句,且都会解析,而预处理只解析一次,后续客户端只传递参数就好,减少了多次解析SQL的损耗,所以执行效率更高。
安全性这一点,也是体现在预处理语句上,预处理语句在传递绑定参数的时候,无论变量参数是什么内容,即使变量参数本身就是一句SQL,也只是当做字符串来处理,这样就大大的降低了数据库SQL注入的危险。
mysql_unbuffered_query 是基于迭代器实现的吗?为什么在内存利用上会更好?
参见: Buffered and Unbuffered queries
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同的类中复用 method。
自定义会话处理机制
一句话删除文件目录
array_map('unlink', glob('*'));
但上述方式未必最好,该方法还是串行执行
怎么并行起来呢?
如果参数中带有"+",会有什么影响?GET方式传参一定要注意。
“+”号是一个提交的变量分隔符,php会自动把他用空格替换了,所以一定要提交前进行转义。
How to Read Big Files with PHP (Without Killing Your Server)
流式处理的典范
生成临时文件有两种办法 php://temp 与 tmpfile,你更爱哪一种?
推荐前者,如果是小文件,则会临时存储在内存中。
php针对多行文本的语法糖 做wordpress开发就会感到很有用了。
php iconv() 编码转换出错 Detected an illegal character
,问题的关键在于每一种编码也是有可表示范围的。
参考: php iconv() : Detected an illegal character in input string
filesize 超过2G文件信息错误处理 没啥实际业务,仅作了解
关注一下字段类型转化可能引起的异常, 比如:
$needle = '1abc';
$haystack = array(1,2,3);
var_dump(in_array($needle, $haystack); //输出为 true
涉及到深浅拷贝的问题, 参考: 对象的复制(拷贝)与__clone()方法
相比于Array,好处在于不需要生成一个数组。
If your code has an existing __autoload() function then this function must be explicitly registered on the __autoload queue.
If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.
PHP读取和解析大文件
Composer除了解决了包管理的问题,也让惰性加载更快更好的落地。不过要了解Composer是如何引入自己编写的库时,需要知道 Psr-4
规范。
- Install Depandence
$ composer install
Cannot create cache directory ~/.composer/cache/repo/https---packagist.org/, or directory is not writable. Proceeding without cache
Cannot create cache directory ~/.composer/cache/files/, or directory is not writable. Proceeding without cache
提示这个目录没有可写权限,composer无法缓存下载的包,这样就每次都得重新下载,把目录改成可写可读即可。
sudo chmod -R 777 ~/.composer
- Remove Depandence
$ composer remove tixastronauta/acc-ip
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 0 installs, 0 updates, 1 removal
- Removing tixastronauta/acc-ip (1.0.0)
Writing lock file
Generating autoload files
- 优化自动加载
composer dump-autoload --optimize
- 切换国内源
composer config -g repo.packagist composer https://packagist.phpcomposer.com
strace -t -e trace=open -o output.log php index.php
# -t 显示调用时间
# -e trace=file 过滤显示
如果需要上传较大文件,可能出现以下错误
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes) in /opt/shujuguan/www/oc/Application/Api/Connectors/Huoban.class.php on line 220
需要修改php底层配置文件中的三个参数, 建议配置大小
memory_limit=512M
post_max_size=256M
upload_max_filesize=256M
第三方(伙伴云)数据更新的时候, php会抛以下错误
PHP Warning: Unknown: Input variables exceeded 35000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0
建议配置
; How many GET/POST/COOKIE input variables may be accepted
; Unknown: Input variables exceeded 1000.
max_input_vars = 40000
到 http://browscap.org/ 下载最新版本的库文件
[browscap]
; http://php.net/browscap
; 配置路径为文件绝对地址
browscap = "/opt/lampp/etc/extra/lite_php_browscap.ini"
支持session数据存储到redis中
display_errors = Off
原来的guzzle5版本已经不再被支持, 需要升级到guzzle6, 但执行中会出现curl证书检查异常, 需要在php.ini中做如下配置:
下载curl官方CA证书 放到/opt/lampp/php/cacert.pem或者某个位置,编辑 php.ini 添加如下配置
[curl] curl.cainfo = "/opt/lampp/php/cacert.pem"
restart apache即可生效
xampp已经集成了zend opcache, 服务器环境下建议直接开启,据说会让PHP执行效率提升6~7倍
zend_extension=opcache.so
; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header). It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
expose_php=Off
session.cookie_domain=.domain.com
内含各种奇淫巧技 :)
MySQL / Redis 因内容较多,该为独立文档维护。
是不是你已厌烦了PHP的同步阻塞IO,那就来看一下利用PHP如何实现原生的非阻塞吧,保证让你有一种恍然大悟的赶脚。
Promise
作为异步编程的解决方案,现在已经在Js代码中烂大街了。其实PHP中也有类似的实现,尤其是 all
方法值得PHP程序员学习借鉴,我们可以以此来达到并发请求的目标,同时也确保了写法的优雅程度。当然我们需要知道的是,PHP中 Promise
的实现也是同步,只是写法看似是异步而已。
AMQP(Advanced Message Queuing Protocol),一个标准的高级消息队列协议。
具体一点,该协议描述的内容包含哪些部分?实现 原理类似于什么?协议实现如何保证可靠性?
心跳的话,基于何种协议实现?
- Session在并发场景下的问题
- 潜在的问题
微服务实现难免需要了解Zookeeper/Etcd,但具体到实现阶段,是否要用PHP实现服务注册和服务发现那就另当别论了,一般会考虑常驻进程的语言,而非PHP。可以参考一下 Service Mesh 的实现。
同时在处理分布式事务的时候也需要依赖Zookeeper的强一致性。可参考:
相比于 Swagger
,apiDoc
对注释的写法要求
获取上传文件大小,如果超过2G的话怎么办?
如何分片以及如何合并分片
玩惯了Node.js的流式处理,感觉PHP处理文件上传过程中先让文件落地的方式实在是让人无奈,那有没有别的办法可以实现流式上传呢?主要是想减少本地磁盘IO
- Web API FileReader
- PHP流式上传和表单上传 走octet-stream上传方式
参考: 文件上传漏洞(绕过姿势)
多线程程序只能运行在cli模式下,而且其实现与其他语言有很大的不同,多个线程之间不能通过共享内存变量的方式进行通信。究其原因,会牵涉到 线程安全 的问题上。
...
共享内存
如何控制内存的使用率?
- 惰性加载
- 变量引用
- 迭代器
请简述Array, ArrayObject和ArrayIterator之间的区别?
如果有看过PDO::query的返回值类型的话,我们会发现,这个方法返回的 PDOStatement,正是对 Iterator 的实现。
php使用引用是否有那么大的好处?毕竟php底层采用写时复制实现。
对buffer高效利用的体现
说到排序真是一把辛酸泪啊,牛逼的公司必问,而且逢考必挂。
常见的排序算法有几种?快排如何实现?堆排序解决了什么问题?能否再举几个应用的示例?
社交聊天,CMS中常用功能,但如何保证检测性能以及误报。
Bloom filter
百度云加速
云锁/安全狗
Web Application Firewall
关注一下渗透测试,看看有没有需要修复的bug
对历史漏洞有个了解
//从360找的可以有效解决PHP安全问题的代码
function StopAttack($StrFiltValue, $ArrFiltReq) {
if (is_array($StrFiltValue)) {
$StrFiltValue = implode($StrFiltValue);
}
if (preg_match("/" . $ArrFiltReq . "/is", $StrFiltValue) == 1) {
print "360websec notice:Illegal operation!";
exit();
}
}
$getfilter = "'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
foreach ($_GET as $value) {
StopAttack($value, $getfilter);
}
$postfilter = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
foreach ($_POST as $value) {
StopAttack($value, $postfilter);
}
$cookiefilter = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
foreach ($_COOKIE as $value) {
StopAttack($value, $cookiefilter);
}
去掉所有PHP文件可执行属性
find ./ -type f -name "*.php" | xargs chmod -x
Sphinx的不足
- 配置繁琐
- 中文分词coreseek不再维护
- 相关度排序实现不太友好
- 无法做到实时更新索引
Solr是新一代的全文检索组件,它比Lucene的搜索效率高很多,还能支持HTTP的访问方式,PHP调用Solr也很方便。
纯真IP官网:http://www.cz88.net/
首先需要将纯真IP库转存为CSV,方便入库
Windows环境下安装最新的版本,并解压为ip.txt文件。使用vi打开ip.txt文件,执行3次以下命令
:%s/\s\+/;/
详解:ip.txt有4列。分别是起始ip,结束ip,地区,说明。列之间用不等数量的空格间隔。为了将此文本文件到入到mysql,需要处理掉这些空格。但是只能处理掉前3列的空格,最后一列中的空格要保留。vi中输入的命令意思是,把每一行第一个连续的空格替换成字符';'。%s代表全局搜索替换。\s代表空格。+代表尽可能多地匹配前面的字符。;代表替换成';'
使用Excel打开处理后的文件,并指定';'为分隔符
多叉树
常用于以下业务场景中:
- 网络爬虫
- 搜索指定格式的文件
- 查找两点间最短路径
- 查看两点间是否存在联系
Wordpress中应用
<html5>
<head>
<style type="text/css">
.hide: {
display: none;
}
</style>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div>
<span class='tab'>所有资源</span>
<span class='tab' data-type='cat1'>解决方案</span>
<span class='tab' data-type='cat2'>视野观点</span>
<span class='tab' data-type='cat3'>可视化技巧</span>
</div>
<ul class="list-items" condition1="" condition2=""></ul>
</body>
<?php
$ret = [
['url' => '/foo', 'img' => 'foo.png', 'title' => 'Foo item', 'cat' => 'cat1'],
['url' => '/foo2', 'img' => 'foo2.png', 'title' => 'Foo item2', 'cat' => 'cat2'],
['url' => '/foo3', 'img' => 'foo3.png', 'title' => 'Foo item3', 'cat' => 'cat3'],
];
?>
<script>
function itemFunc(currentValue, index, arr) {
/* return `<a href="${currentValue.url}" class="list-group-item">
<div class="image">
<img src="${currentValue.img}" />
</div>
<p class="list-group-item-text">${currentValue.title}</p>
</a>
`; */
return '<li class="' + currentValue.cat + '"><a href="' + currentValue.url + '"><div class="image"><img src="' + currentValue.img + '" /></div><p class="list-group-item-text">' + currentValue.title + '</p></a></li>';
}
function filterItems() {
var condition1 = $('.list-items').attr('condition1');
var condition2 = $('.list-items').attr('condition2');
$('.list-items > li').show();
if (!condition1 && !condition2) return;
$('.list-items > li').each(function(index, item) {
if (!$(item).hasClass(condition1)) {
$(item).hide();
}
});
}
// var fragment = document.createDocumentFragment();
/*for (var i = 0; i < 50; i++) {
var tmpNode = document.createElement("div");
tmpNode.innerHTML = "test" + i;
fragment.appendChild(tmpNode);
}*/
/*document.body.appendChild(fragment);*/
$(function() {
/*var ajaxUrl = 'demo_test.php';
$.get(ajaxUrl, function(data, status) {
console.log('数据: ' + data + '\n状态: ' + status);
var list = data.data;
var list = [{
url: '/foo',
img: 'foo.png',
title: 'Foo item'
}, {
url: '/bar',
img: 'bar.png',
title: 'Bar item'
}, ];
$('.list-items').html(list.map(itemFunc).join(''));
});
*/
var list = <?php echo json_encode($ret); ?>;
$('.list-items').html(list.map(itemFunc).join(''));
$('.tab').click(function() {
var condition = $(this).attr('data-type') || '';
$('.list-items').attr('condition1', condition);
filterItems()
})
});
</script>
</html5>
两种策略
- 真静态
- 伪静态
- Nginx Rewrite
- 路由解析
真静态的优点
- 访问速度快(不用调用php解析模块,不需要查询数据库)
- 利于SEO
- 防止SQL注入
由此可以看出门户网站最需要静态化
在以下情况下不建议使用真静态
- 网站的实时性比较高,也就是说查询频繁(股票,基金)
- 查询一次后,以后很少查询(国家学历认证网,电信话费查询系统)
按照城市划分子站,其中有哪些考虑要素?
最好的办法是从你们公司自己的DNS解析去设置,这是效率最高的。还可以在你们所有服务器前端搭一个反向代理,比如Nginx,它有个扩展模块好像叫geo的模块,可以从这里配置,不同地区的ip段代理到不同的分站。最差的方法就是rewrite。三种方式都可以实现。
参考一下 Monolog,但此处更重要的是观察者模式如何实现?以及日志能否异步处理或者多观察者并行处理?
使用钩子可以更好的做到切面编程,说到底就是面向接口的编程。常见的Wordpress中就内置了很多钩子,可以看一下 WordPress Actions and Filters