PHP最佳实践

PHP关于应用架构、规划、数据库、安全、测试、调试和部署方面的行业标杆。

概述

3年前,我刚转做WEB开发,用PHP做了几个小网站、公众号和WEB APP以后,对PHP的印象可以用4个字形容——累觉不爱。

从手动引入自己写的代码文件,到引用产品的某个函数,到记录日志,到缓存数据,到发邮件不卡住,每换一个产品,这些基础的问题都要重新解决一遍,重复开发基础的功能(比如日志),重复写机械的代码让我实在很崩溃。

在我的观念里,软件开发不应该这样的,应用层面的软件开发发展到今天,开发者应该可以做到将注意力集中在在业务问题上,而不是再陷入语言特性和基础设施上。有一段时间我很矛盾,是否该考虑转向JAVA、Python、Node试试了。

现代PHP的出现,结束了这个矛盾的过程,那些前面令人饱受煎熬的问题,都得到了解决。

包管理、自动加载,PSR标准,以及日志、缓存、队列、命令行作为基础设施的组件,使得这个古老的语言脱胎换骨。在有了这些现代化特征后,我才第一次感觉到自己是在关注业务,而不用像之前为了让发送邮件不卡住而专门写一个简易队列。

因此,现代PHP之所以重要,就在于它将你从重复劳动中解放出来,将注意力更多地集中在具体的业务上。

开发:关注业务

在前面“现代PHP为什么重要”其实对这个问题已经有了一定的探讨。现代PHP的包管理、自动加载、PSR标准,以及日志、缓存、队列等各种组件,都是为了让开发者可以将注意力集中在解决业务问题上。

没有这些现代化特征,实际开发最容易碰到的问题就是重复开发基础功能。比如写日志时,发现没接口,于是自己实现一套简易的;发邮件不想阻塞请求,于是自己写了一个简易队列。花在写基础设施的时间比花在解决具体业务的时间都多。而且还不稳定。

工程管理:测试、部署、分析都有标准化的工具

“在过去,常见的做法是编写一个PHP文件,使用FTP上传到生产服务器,然后祈祷它能正常运行。这种开发策略非常可怕,但又必须这么做,因为当时没有可用的本地开发环境。”WEB开发是个工程管理问题比技术问题多的领域,沿用上述的这种工程管理策略,能把自己玩吐血。

现代的PHP项目的标配采用的是最佳实践的工程管理方式。

  1. 用Git做代码版本控制管理(不再使用不可靠的FTP):帮助我们维护一个可审查的代码历史,让我们可以创建代码分支、复刻(fork)代码和合并代码。

  2. 用虚拟化工具(例如Vagrant)以及配置工具(例如Ansible、Chef和Puppet),搭建和生产服务器一样的本地开发环境

  3. 通过依赖管理工具Composer使用专门的PHP组件。

  4. 在代码推送完成后通过CI/CD自动完成测试和布署。如果布署有问题,则要立刻回滚。

  5. 在性能分析上,则通过xhprof或者new relic这样的工具去分析并改进。

  6. PHP代码遵循PSR,这是由PHP Framework Interop Group管理的社区标准。

  7. 使用PHPUnit等工具彻底测试编写的代码。

  8. 使用PHP的FastCGI进程管理器部署应用,并且放在nginx这样的Web服务器之后。

  9. 使用操作码缓存来提升应用的性能。

使用接口

依赖接口,而非具体实现,可以解耦,参考 Laravel 的契约

性状(trait)

这个特性很有意思,名字名副其实。

假如现在有一个人,还有一辆车,车和人都要实现给自己定位的功能,显然车和人不适合从一个父类继承,那这个时候 trait 就排上用场了。这是一个横向复用机制,让若干不想关的类带上同样一个性状。

http://php.net/manual/zh/language.oop5.tra...

生成器(yield)

这个特性也很有用,yield 和 return 类似,区别在于 yield 只产出值,不返回值。

也可以这样理解,return 是一次性全部交付给你,yield 是承诺全部交付给你,但这次先把你现在用到的东西给你,剩下的下次你再来取。

显然,在迭代一个超大数据的时候(遍历超大数组),yield 可以先给你一个元素,不用生成整个数组,可以省内存。

上代码:

<?php
function makeRange($length){

    $arr=[];

    for($i=0;$i<$length;$i++) $arr[]=$i;

    return $arr;

}

$results=makeRange(100000);

foreach($results as $i) echo $i.PHP_EOL;  
<?php

function makeRange($length){

    $arr=[];

    for($i=0;$i<$length;$i++) yield $i;

}

foreach(makeRange(100000) as $i) echo $i.PHP_EOL;

第一个是先生成一整个数组再遍历,第二个是一边生成一边遍历。

http://php.net/manual/zh/language.generato...

闭包,命名空间,opcache

PSR,组件,composer

这三个东西对现代 PHP 意义特别重大,选择可复用组件就是选择了未来,细分领域让最专业的人去做。

过滤,验证,转义(防注入和防XSS)

输入和输出要过滤 html,使用 htmlentities($str, ENT_QUOTES, "UTF-8"); 第二个参数开启转义单双引号,第三个参数指明字符集,都是必须开启的。

使用 PHP 自带过滤和验证函数

filter_input()

http://php.net/manual/zh/function.filter-i...

filter_var()

http://php.net/manual/zh/function.filter-v...

数据库

使用 PDO 和预处理

密码

使用内置的密码函数,使用bcrypt 加密

password_hash()

http://php.net/manual/zh/function.password...

password_verify() http://php.net/manual/zh/function.password...

多字节字符串

使用 mb 系列函数

流处理,错误和异常,自动部署,持续测试

参见

  1. 《Modern PHP》(中文版) 高清pdf

  2. Modern PHP 笔记(一):语言特性

  3. Modern PHP 笔记(二):良好实践

  4. Modern PHP 笔记(三):部署测试和调优