最近,我偶尔有机会使用几个旧的PHP应用程序。我注意到一些必须修复的常见反模式。本文不是关于如何重写旧的PHP应用程序以“在此处插入精彩框架的名称”,而是关于如何使其更易于维护和使用的麻烦。
反模式#1:代码中的凭证
这是我遇到的最常见的最坏模式。在许多项目中,版本化代码会使用重要信息进行硬编码,例如用于访问数据库的名称和密码。显然,这是一种不好的做法,因为它不允许创建本地环境,因为代码已绑定到特定的环境。此外,任何有权访问代码的人都可以看到通常适用于生产环境的凭据。
要解决此问题,我更喜欢一种适用于任何应用程序的方法:安装phpdotenv软件包,该软件包允许您创建环境文件并使用环境超级变量访问变量。
让我们创建两个文件:
.env.example
一个将被版本化并用作该文件的模板.env
,其中将包含凭据。该文件.env
未进行版本控制,因此将其添加到中.gitignore
。官方文档对此进行了很好的解释。
您的文件
.env.example
将列出凭据:
DB_HOST=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
数据本身将在文件中
.env
:
DB_HOST=localhost
DB_DATABASE=mydb
DB_USERNAME=root
DB_PASSWORD=root
在常规文件中,加载
.env
:
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
然后可以使用说来将其应用于会计数据
$_ENV['DB_HOST']
。
不建议在“按原样”操作中使用该软件包,因为这样做更好:
- 如果您具有基于Docker的部署,则将环境变量注入到容器的运行时中;如果可能,请将环境变量注入到服务器端HTTP配置中。
- 缓存环境变量,以避免在每个请求上读取.env的开销。Laravel就是这样做的。
凭证文件可以从Git历史记录中删除。
反模式2:请勿使用Composer
以前,拥有带有大型库(如PHPMailer)的lib文件夹非常流行。在进行版本控制时,应以各种可能的方式避免这种情况,因此应使用Composer管理这些依赖项。然后,您将很容易查看正在使用哪个版本的软件包,并在必要时进行更新。
因此,安装Composer并使用它进行管理。
反模式3:没有本地环境
我使用的大多数应用程序只有一个环境:生产。
但是,通过摆脱1号反模式,您可以轻松自定义本地环境。您可能已经对一些配置进行了硬编码,例如引导路径,但是现在您可以将其移动到
.env
。
我使用Docker创建本地环境。它对于较旧的项目特别有效,因为它们经常使用不需要或无法安装的较旧版本的PHP。
您可以使用PHPDocker之类的服务,也可以使用一个小文件
docker-compose.yml
。
反模式4:请勿使用公用文件夹
事实证明,大多数这些旧项目可从其根文件夹访问。也就是说,根目录中的任何文件都可以公开读取。当攻击者(例如小子脚本)尝试直接访问包含的文件时,这尤其糟糕,因为如果脚本直接访问所有包含的文件,则您可能无法确定输出。
显然,这种情况与使用
.env
或Composer不兼容,因为打开vendor文件夹是一个坏主意。是的,这样做有一些技巧。但是,如果可能,请将所有对客户端开放的PHP文件移动到一个文件夹中,Public
并更改服务器配置,以使该文件夹成为您的应用程序的根文件夹。
我通常这样做:
docker
为Docker相关文件(Nginx配置,PHP Dockerfile等)创建一个文件夹。- 我创建一个
app
用于存储业务逻辑(服务,类等)的文件夹。 - 我创建一个文件夹,
public
在其中存储对客户端开放的PHP脚本和资源(JS / CSS)。从客户端的角度来看,这是应用程序的根文件夹。 - 我创建文件
.env
和.env.example
。
反模式5:严重的安全问题
PHP应用程序,尤其是不使用框架的较旧应用程序,通常会遭受严重的安全性问题:
- 由于查询中缺少参数的转义,因此存在SQL注入的危险。为了防止它们,请使用PDO!
- 由于显示了非转义的用户数据,因此存在XSS注入的危险。使用htmlspecialchars阻止它们。
- … . , , , .
- - CSRF-. Anti-CSRF, .
- 密码加密不良。我已经看到许多项目仍然使用SHA-1甚至MD5进行密码哈希处理。开箱即用的PHP 5.5对BCrypt具有良好的支持,不使用它很可惜。为了舒适地传输密码,我更喜欢在用户登录时更新数据库中的哈希。最主要的是确保该列
password
足够长以容纳BCrypt密码,VARCHAR(255)可以。这是伪代码,使其更清晰:
<?php // : $ // : if (strpos($oldPasswordHash, '$') !== 0 && hash_equals($oldPasswordHash, sha1($clearPasswordInput))) { $newPasswordHash = password_hash($clearPasswordInput, PASSWORD_DEFAULT); // password // : } // if (password_verify($clearPasswordInput, $currentPasswordHash)) { // : } // :
反模式6:无测试
这在较旧的应用程序中很常见。几乎不可能开始为整个应用程序编写单元测试,因此可以编写功能测试。
这些是高级测试,可帮助您确保应用程序的后续重构不会破坏应用程序。测试可能很简单,例如,我们启动浏览器并输入应用程序,然后等待操作成功的HTTP代码和/或最后一页上的相应消息。对于测试,您可以使用PHPUnit或Cypress或codeception。
反模式7:差的错误处理
如果(或很有可能在什么时候)发生故障,则需要快速查找。但是许多较旧的应用程序不能很好地处理错误,它们依赖PHP保持宽大处理。
您需要能够捕获并记录尽可能多的错误以进行修复。关于这个话题有好文章。
此外,如果系统引发特定异常,您将更容易找到发生错误的位置。
反模式8:全局变量
我以为,直到我开始处理旧项目之前,我再也看不到它们了。全局变量使对代码行为的读取和理解变得不可预测。简而言之,这是邪恶的。
最好改用Dependency Injection ,因为它使您可以控制使用哪些实例以及在何处使用实例。例如,Pimple程序包表现良好。
还有什么要改进的?
根据应用程序的命运或预算的不同,您可以采取更多其他步骤来改进项目。
首先,如果应用程序在较低版本的PHP(低于7)上运行,请尝试对其进行更新。通常,这不会引起大问题,最重要的是,大部分时间都需要摆脱呼叫
mysql_ calls
(如果有的话)。为了快速解决此问题,您可以使用类似的库,但是最好重写所有对PDO的请求,以便同时转义所有参数。
如果应用程序不使用MVC模式,即业务逻辑和模板是分开的,那么该添加模板库了(我知道PHP是一种模板语言,但是现代的库要方便得多),例如Smarty,Twig或Blade。
最后,从长远来看,最好在现代的PHP框架(如Laravel或Symfony)中重写应用程序。您将拥有安全和智能PHP开发所需的所有工具。如果应用程序很大,那么我建议使用扼杀器模式来避免大爆炸重写,否则可能会(并且很可能会)导致糟糕的结果。因此,您可以将当前正在处理的代码部分迁移到新系统中,使旧的工作部分保持完整,直到它们被处理为止。
这是一种有效的方法,可让您为日常工作创建现代的PHP环境,而无需冻结功能数周或数月,具体取决于项目。