PHP是一门非常流行的服务端编程语言,被广泛用于Web开发。但是,PHP的灵活性也导致了很多PHP代码质量不高,存在各种问题,如代码混乱、性能低下、安全漏洞等。

作为一个PHP开发者,写出优雅、高效、安全的代码,是我们的基本要求。今天就来分享一些PHP编程的最佳实践,帮助大家写出更好的PHP代码。

代码规范

1. 使用PSR规范

PSR(PHP Standards Recommendations)是PHP-FIG制定的PHP编码规范,包括PSR-1(基础编码规范)、PSR-2(编码风格规范)、PSR-4(自动加载规范)等。遵循PSR规范,能够让代码更规范、更易读、更易维护,也方便和其他开发者协作。

PSR-1基础规范:PHP代码必须使用标准PHP标签;文件名必须使用大驼峰命名;类名必须使用大驼峰命名;类的常量必须使用大写字母加下划线;方法名必须使用小驼峰命名。

PSR-2编码风格规范:代码必须使用4个空格缩进,不能使用Tab;每行代码长度建议不超过120个字符;必须在namespace声明后空一行,use声明后空一行;类和方法的开始花括号必须写在声明后换行;控制结构的开始花括号必须写在同一行。

PSR-4自动加载规范:命名空间必须与目录结构对应;类名必须与文件名对应;自动加载器不能抛出异常,不能触发任何级别的错误。

2. 合理命名

合理的命名是代码可读性的基础。变量、函数、类、常量等的命名,要简洁、明确、有意义,让人一看就知道是什么意思。

命名原则:变量名和函数名使用小驼峰(camelCase),如userName、getUserInfo;类名使用大驼峰(StudlyCaps),如UserModel、OrderController;常量使用大写字母加下划线,如MAXRETRYCOUNT、DEFAULTPAGESIZE;命名要有意义,不要使用无意义的名字如a、b、temp;命名要简洁,不要太长也不要太短;布尔变量和函数使用is、has、can等前缀,如isValid、hasPermission;数组变量使用复数形式,如users、orders。

3. 合理注释

注释是代码的重要组成部分,能够帮助别人理解代码的意图和逻辑。但是,注释不是越多越好,过多的注释反而会干扰阅读。

注释原则:注释要简洁明了,不要写废话,不要重复代码已经表达的意思;注释要解释"为什么",而不是"做什么",代码本身已经说明了做什么,注释应该说明为什么这么做;复杂的逻辑、算法、业务规则要加注释说明;公共的类、方法、函数要加文档注释(PHPDoc),说明参数、返回值、异常等;不要写过时的注释,代码修改后要及时更新注释;不要用注释掉的代码,没用的代码直接删掉。

代码质量

1. 单一职责原则

单一职责原则(Single Responsibility Principle,SRP)是SOLID原则之一,指的是一个类或函数只负责一件事情,只有一个引起它变化的原因。

单一职责原则的好处:代码更清晰,每个类和函数的职责明确,容易理解;代码更易维护,修改一个功能只需要修改对应的类和函数,不会影响其他功能;代码更易测试,每个类和函数的职责单一,容易编写单元测试。

如果一个函数做了太多事情,比如获取订单、获取用户、发送邮件、扣减库存、创建发票、更新订单状态都在一个函数里,就违反了单一职责原则。应该把每个功能拆分成独立的函数,每个函数只做一件事情。

2. 避免重复代码

重复代码(DRY,Don't Repeat Yourself)是代码质量的大敌。重复的代码不仅增加了代码量,还增加了维护成本,修改一个功能需要修改多处,容易遗漏,导致bug。

避免重复代码的方法:把重复的逻辑抽取成函数或方法;把重复的属性和方法抽取成父类或trait;把重复的配置抽取成配置文件或常量;使用设计模式(如工厂模式、策略模式等)消除重复。

比如,获取用户信息的逻辑在多个函数中重复出现,就应该把它抽取成一个独立的getUserById函数,其他函数调用这个函数即可,不要重复写查询逻辑。

3. 函数参数不要太多

函数的参数不要太多,一般建议不超过3-4个。参数太多的函数,调用时容易出错,也难以理解和维护。

如果函数确实需要很多参数,可以考虑:把参数封装成一个对象或数组;把函数拆分成多个小函数;使用建造者模式或工厂模式。

比如,创建用户的函数需要name、email、password、age、gender、phone、address、avatar等8个参数,就可以把这些参数封装成一个User对象或一个数组,函数只接收一个参数即可。

4. 函数不要太长

函数不要太长,一般建议不超过50行。太长的函数,逻辑复杂,难以理解和维护,也难以测试。

如果函数太长,可以考虑:把函数拆分成多个小函数,每个小函数只做一件事情;把相关的逻辑抽取成类;使用设计模式简化逻辑。

5. 避免全局变量

全局变量会导致代码耦合度高,难以测试和维护,也容易产生意外的副作用。尽量避免使用全局变量,使用函数参数和返回值传递数据。

如果确实需要共享数据,可以考虑:使用类的属性;使用依赖注入;使用单例模式(谨慎使用);使用配置类。

性能优化

1. 使用OPcache

OPcache是PHP的字节码缓存扩展,能够把PHP脚本编译后的字节码缓存起来,避免每次请求都重新编译,大大提高PHP的性能。生产环境一定要开启OPcache。

OPcache配置要点:开启opcache.enable;设置合理的内存大小(如128MB);设置合理的最大加速文件数(如10000);设置合理的重新验证频率(如60秒);开启快速关闭。

2. 合理使用缓存

缓存是提高Web应用性能的重要手段。对于不经常变化的数据,如配置信息、文章列表、热门文章等,可以缓存起来,避免每次都查询数据库。

常用的缓存方案:文件缓存(把数据缓存到文件中,适合小型应用);Memcached(分布式内存缓存系统,适合大型应用);Redis(内存数据结构存储,支持多种数据结构,功能强大,是目前最流行的缓存方案)。

缓存注意事项:设置合理的过期时间,避免数据过期;数据更新时及时清除缓存,避免缓存和数据库不一致;防止缓存穿透(查询不存在的数据,缓存没有命中,每次都查询数据库);防止缓存雪崩(大量缓存同时过期,导致数据库压力骤增);防止缓存击穿(热点数据过期,大量请求同时查询数据库)。

3. 优化数据库查询

数据库查询往往是Web应用的性能瓶颈。优化数据库查询,能够大大提高应用的性能。

数据库查询优化方法:给经常查询的字段添加索引,如主键、外键、WHERE条件字段、ORDER BY字段、GROUP BY字段;避免SELECT星号,只查询需要的字段;避免在WHERE条件中使用函数或运算,导致索引失效;避免使用子查询,尽量使用JOIN;合理使用LIMIT,避免查询大量数据;使用EXPLAIN分析查询语句,查看索引使用情况;避免N+1查询问题,使用预加载或JOIN。

N+1查询问题是指先查询出10篇文章,然后循环每篇文章查询对应的用户信息,这样就执行了1次文章查询+10次用户查询=11次查询。优化方法是使用JOIN一次查询出文章和用户信息,只需要1次查询。

4. 减少文件包含

PHP的文件包含(include、require)有一定的性能开销。尽量减少文件包含,使用自动加载(splautoloadregister),只在需要时才加载文件。不要在循环中包含文件,不要包含不需要的文件。

5. 合理使用PHP版本

新版本的PHP通常有更好的性能和更多的特性。尽量使用较新的PHP版本,如PHP 7.4、PHP 8.0等,它们的性能比PHP 5.x有大幅提升。但是,升级PHP版本时要注意兼容性,确保代码和扩展能够在新版本上正常运行。

安全实践

1. 防止SQL注入

SQL注入是最常见的安全漏洞之一。用户输入的数据如果直接拼接到SQL语句中,就可能被恶意利用,执行非法的SQL操作。

防止SQL注入的方法:使用PDO或MySQLi的预处理语句(Prepared Statement),不要直接拼接SQL;对用户输入进行验证和过滤;使用ORM框架(如Eloquent、Doctrine),自动处理SQL注入。

预处理语句的原理是先把SQL语句模板发送给数据库编译,然后再发送参数,参数会被当作纯数据处理,不会被当作SQL代码执行,从而防止SQL注入。

2. 防止XSS攻击

XSS(跨站脚本攻击)是指攻击者在网页中注入恶意脚本,当用户访问网页时,恶意脚本会在用户的浏览器中执行,窃取用户信息或进行其他恶意操作。

防止XSS攻击的方法:对用户输入进行过滤,移除或转义HTML标签;对输出到HTML的内容进行转义(使用htmlspecialchars函数);使用CSP(内容安全策略),限制网页可以加载的资源;设置HttpOnly Cookie,防止JavaScript读取Cookie。

htmlspecialchars函数会把特殊字符(如小于号、大于号、引号、和号)转换为HTML实体,从而防止浏览器把它们当作HTML标签或脚本执行。

3. 防止CSRF攻击

CSRF(跨站请求伪造)是指攻击者诱导用户在已登录的网站上执行非本意的操作,如转账、修改密码等。

防止CSRF攻击的方法:使用CSRF Token,在表单中添加一个随机的Token,提交时验证Token;验证Referer,检查请求的来源是否合法;敏感操作使用POST方法,不要使用GET方法;敏感操作需要二次验证(如输入密码、验证码)。

CSRF Token的原理是在用户访问表单页面时生成一个随机的Token存储在Session中,同时在表单中添加一个隐藏字段包含这个Token,表单提交时验证提交的Token和Session中的Token是否一致,不一致则拒绝请求。

4. 密码安全

密码是用户账户安全的关键。存储和验证密码时,一定要使用安全的方式。

密码安全实践:不要明文存储密码,一定要使用哈希算法加密;使用bcrypt或Argon2等安全的哈希算法,不要使用MD5、SHA1等不安全的算法;加盐(Salt),每个密码使用不同的盐,防止彩虹表攻击;密码验证使用专门的验证函数,不要自己比较哈希值;强制用户使用强密码(长度、复杂度);敏感操作需要重新输入密码。

bcrypt算法是目前最常用的密码哈希算法,它内置了盐和工作因子,能够有效抵抗暴力破解和彩虹表攻击。PHP的passwordhash函数默认使用bcrypt算法,passwordverify函数用于验证密码。

5. 文件上传安全

文件上传是常见的功能,也是常见的安全漏洞。如果文件上传处理不当,攻击者可能上传恶意文件(如webshell),控制服务器。

文件上传安全实践:验证文件类型,不要只看扩展名,要验证文件的MIME类型和文件内容;重命名上传的文件,不要使用用户提供的文件名;把上传的文件存储在Web根目录之外,或者限制上传目录的执行权限;设置上传文件的大小限制;不要把上传目录设置为可执行权限;对上传的图片进行重新生成(使用GD或ImageMagick),去除图片中的恶意代码。

错误处理

1. 合理设置错误报告级别

开发环境开启所有错误报告,方便调试;生产环境关闭错误显示,只记录错误日志,避免泄露敏感信息。

开发环境配置:errorreporting设置为EALL,显示所有错误;displayerrors设置为On,在页面上显示错误;displaystartup_errors设置为On,显示启动错误。

生产环境配置:errorreporting设置为EALL减去已废弃和严格类型错误;displayerrors设置为Off,不在页面上显示错误;logerrors设置为On,把错误记录到日志文件;error_log设置为日志文件路径。

2. 使用异常处理

使用异常(Exception)处理错误,而不是使用返回值或die/exit。异常能够提供更详细的错误信息,也更容易处理和调试。

异常处理的基本结构是try-catch:在try块中执行可能出错的代码,如果出错就抛出异常,在catch块中捕获并处理异常。可以有多个catch块捕获不同类型的异常,也可以有finally块无论是否出错都会执行。

3. 不要忽略错误

不要使用错误抑制符(@)忽略错误,不要空的catch块,不要忽略函数的返回值。忽略错误会导致bug难以发现和调试。

使用@抑制错误会把所有错误都隐藏起来,包括致命错误,导致程序出错了也不知道为什么。空的catch块会捕获异常但是不做任何处理,相当于忽略了错误。忽略函数的返回值可能导致错误没有被及时发现。

测试

1. 编写单元测试

单元测试是保证代码质量的重要手段。为核心的函数和类编写单元测试,能够在修改代码时快速发现问题,避免引入新的bug。

常用的PHP测试框架:PHPUnit(最流行、最标准的测试框架)、PHPSpec(行为驱动开发框架)、Codeception(全栈测试框架,支持单元测试、功能测试、验收测试)。

单元测试的原则:每个测试只测试一个功能;测试之间相互独立,不依赖执行顺序;测试要覆盖正常情况、边界情况、异常情况;测试代码要简洁、清晰、易读。

2. 编写集成测试

除了单元测试,还要编写集成测试,测试多个模块之间的协作,确保整个系统能够正常运行。集成测试关注的是模块之间的接口和数据流转,而不是单个函数的逻辑。

3. 代码审查

代码审查(Code Review)是保证代码质量的重要手段。提交代码前,让其他开发者审查代码,能够发现自己看不到的问题,也能够互相学习,提高团队整体的代码水平。

代码审查的要点:检查代码是否符合编码规范;检查代码逻辑是否正确,是否有bug;检查代码是否有安全漏洞;检查代码性能是否有问题;检查代码是否易读、易维护;提出改进建议,而不是指责。

总结

PHP编程最佳实践包括很多方面:代码规范(PSR规范、合理命名、合理注释)、代码质量(单一职责原则、避免重复代码、函数参数不要太多、函数不要太长、避免全局变量)、性能优化(使用OPcache、合理使用缓存、优化数据库查询、减少文件包含、合理使用PHP版本)、安全实践(防止SQL注入、防止XSS攻击、防止CSRF攻击、密码安全、文件上传安全)、错误处理(合理设置错误报告级别、使用异常处理、不要忽略错误)、测试(编写单元测试、编写集成测试、代码审查)。

写出优雅、高效、安全的PHP代码,不是一朝一夕的事情,需要我们在日常开发中不断学习、不断实践、不断总结。希望这些最佳实践能够帮助大家写出更好的PHP代码,提高代码质量,减少bug,提高开发效率。

PHP是一门不断发展的语言,新的特性和最佳实践也在不断出现。作为PHP开发者,要持续学习,跟上技术的发展,掌握最新的PHP特性和最佳实践,写出更优雅、更高效、更安全的代码。