现代化旧的PHP应用程序



最近,我偶尔有机会使用几个旧的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或Cypresscodeception



反模式7:差的错误处理



如果(或很有可能在什么时候)发生故障,则需要快速查找。但是许多较旧的应用程序不能很好地处理错误,它们依赖PHP保持宽大处理。



您需要能够捕获并记录尽可能多的错误以进行修复。关于这个话题有好文章



此外,如果系统引发特定异常,您将更容易找到发生错误的位置。



反模式8:全局变量



我以为,直到我开始处理旧项目之前,我再也看不到它们了。全局变量使对代码行为的读取和理解变得不可预测。简而言之,这是邪恶的



最好改用Dependency Injection ,因为它使您可以控制使用哪些实例以及在何处使用实例。例如,Pimple程序包表现良好



还有什么要改进的?



根据应用程序的命运或预算的不同,您可以采取更多其他步骤来改进项目。



首先,如果应用程序在较低版本的PHP(低于7)上运行,请尝试对其进行更新。通常,这不会引起大问题,最重要的是,大部分时间都需要摆脱呼叫mysql_ calls(如果有的话)。为了快速解决此问题,您可以使用类似的库,但是最好重写所有对PDO的请求,以便同时转义所有参数。



如果应用程序不使用MVC模式,即业务逻辑和模板是分开的,那么该添加模板库了(我知道PHP是一种模板语言,但是现代的库要方便得多),例如Smarty,Twig或Blade。



最后,从长远来看,最好在现代的PHP框架(如Laravel或Symfony)中重写应用程序。您将拥有安全和智能PHP开发所需的所有工具。如果应用程序很大,那么我建议使用扼杀器模式来避免大爆炸重写,否则可能会(并且很可能会)导致糟糕的结果。因此,您可以将当前正在处理的代码部分迁移到新系统中,使旧的工作部分保持完整,直到它们被处理为止。



这是一种有效的方法,可让您为日常工作创建现代的PHP环境,而无需冻结功能数周或数月,具体取决于项目。



All Articles