PHP项目中为什么越来越多人放弃原生开发转向框架化

各位朋友,大家晚上好!

欢迎大家来到我的讲座现场。先问大家一个问题:你们现在写PHP代码,还在用纯原生的 index.php 里面堆几百行 if 判断,最后在一个巨大的 switch 语句里调数据库吗?

如果你的答案是“是的”,那么恭喜你,你有机会去体验一下什么是“重铸辉煌”,或者叫“把自己折磨疯”。

如果你已经转向了 Laravel、Symfony 甚至 Symfony 的近亲,或者哪怕是 ThinkPHP、CodeIgniter 这种,那你也是我今晚要重点交流的对象——当然,也就是你们这群“现代人”。

今天我们要聊的话题很硬核,也很扎心:为什么在这个时代,越来越多的PHP开发者,毅然决然地扔掉了原生开发的拐杖,一头扎进了框架的怀抱?

这不仅仅是为了“时髦”,更是为了“保命”。

首先,我们要承认一个事实。在PHP的早期,或者说很多新手眼里,PHP就是写写脚本,搞搞后台。确实,它能做。但你有没有试过,在一个只有几百行的 index.php 文件里,一边处理用户登录,一边发邮件,一边写日志,一边还要去修改一个已经存在了五年的 global $db 变量?

这时候你就会发现,原生PHP就像是一团没煮过的意大利面,虽然你能把它塞进嘴里,但如果你不小心把酱汁(业务逻辑)和面条(代码结构)搅在一起了,那就是真正的“乱码”。

第一部分:原生PHP的“地狱厨房”

让我们先来痛批一下原生开发。为什么大家想跑?

1. 全局变量的幽灵

在原生PHP里,最可怕的东西莫过于 global 关键字。它就像是一个游荡在代码宇宙中的幽灵,你永远不知道你的变量 $_GET['user_id'] 是从哪来的,是不是被上一个函数改了,或者是不是根本就是你的幻觉。

看看下面这段代码,这是很多资深开发者在深夜独自面对的噩梦:

<?php
// index.php - 一个通用的入口文件

// 连接数据库(还没封装好)
$host = 'localhost';
$user = 'root';
$pass = '123456';
$db   = 'my_shop';
$conn = mysqli_connect($host, $user, $pass, $db);
if (!$conn) { die("Connection failed: " . mysqli_connect_error()); }

// 处理路由(没有任何路由概念)
$path = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];

if (strpos($path, '/user/login') !== false) {
    // 处理登录逻辑
    $input = json_decode(file_get_contents('php://input'), true);
    $username = $input['username'];
    $password = $input['password'];

    // 查询数据库
    $sql = "SELECT * FROM users WHERE username = '$username'";
    $result = mysqli_query($conn, $sql);
    $row = mysqli_fetch_assoc($result);

    if ($row && password_verify($password, $row['password'])) {
        $_SESSION['user_id'] = $row['id'];
        echo json_encode(['status' => 'success']);
    } else {
        http_response_code(401);
        echo json_encode(['status' => 'error', 'msg' => 'Login failed']);
    }
} elseif (strpos($path, '/user/profile') !== false) {
    // 处理个人资料(又是重复的连接数据库逻辑)
    if (!isset($_SESSION['user_id'])) {
        http_response_code(403);
        echo "Access Denied";
        exit;
    }

    // 这里的逻辑跟上面一模一样,只是换个SQL
    $user_id = $_SESSION['user_id'];
    $sql = "SELECT * FROM users WHERE id = $user_id";
    $result = mysqli_query($conn, $sql);
    $row = mysqli_fetch_assoc($result);

    echo json_encode($row);
} else {
    http_response_code(404);
    echo "404 Not Found";
}

// 关闭连接(别忘了)
mysqli_close($conn);
?>

看完了这段代码,是不是感觉头皮发麻?这就是原生的“味道”。它把路由、业务逻辑、数据库操作、Session管理全塞在一个文件里。

痛点在于:

  1. 重复造轮子: 每次都要手动 mysqli_connect,每次都要判断 if (strpos...)
  2. 没有命名空间: 几个文件之后,你的 $user 变量可能是一个对象,也可能是一个数组,全靠脑子记。
  3. 难以测试: 你想测试 login 函数?行啊,你得把整个数据库、Session、HTTP请求都模拟出来,或者写个脚本手动 $_POST 数据。但在原生代码里,函数是紧紧耦合在全局变量上的,你根本无法单独测试它。
  4. “面条式代码”: 这种代码结构就像面条一样,没有形状,你不知道哪头是头,哪头是尾。

当你接到一个需求:“老板,把这个登录页面的密码强度验证加一下。” 你得去哪加?去那个几万行的 index.php 里,用Ctrl+F搜索 $password?万一搜到了三个 $password,哪个是登录的?哪个是修改密码的?

这时候,框架就是你的手术刀,它把这块“烂肉”切开了。

第二部分:框架,是给代码穿上了西装

为什么要用框架?框架不是为了炫技,而是为了工程化。它给你的代码穿上了西装,打了领带,让你看起来像是在写一个正经的、能上线的、能对接团队的产品,而不是在垃圾堆里捡代码。

1. MVC架构:让代码各司其职

框架的核心灵魂通常是 MVC(Model-View-Controller)。这听起来是个高大上的词,其实就是把“厨房”拆开了。

  • Model(模型): 专门管数据库。你不用管怎么连接,不用管SQL写错没,你只管写逻辑,比如 User::find(1)
  • View(视图): 专门管怎么展示。就是那个 .php 文件,负责把数据变成HTML。
  • Controller(控制器): 专门管指挥。它接收用户的请求,然后告诉模型去查数据,告诉视图去渲染页面。

让我们看看同一段逻辑,用 Laravel 框架(目前PHP界的霸主)写出来是什么样子。

路由层:

// routes/api.php
use AppModelsUser;
use IlluminateSupportFacadesRoute;

// 路由就是路标,告诉系统:凡是访问 /api/login 的,都交给 LoginController 处理
Route::post('/login', [LoginController::class, 'login']);

控制器层:

// app/Http/Controllers/LoginController.php
namespace AppHttpControllers;

use AppModelsUser;
use IlluminateHttpRequest;

class LoginController extends Controller
{
    // 这里是一个方法,专注做登录一件事
    public function login(Request $request)
    {
        // 数据验证(框架自带)
        $validated = $request->validate([
            'username' => 'required|string',
            'password' => 'required|string',
        ]);

        // 查找用户(ORM自动处理SQL和转义,防注入)
        $user = User::where('username', $validated['username'])->first();

        if ($user && password_verify($validated['password'], $user->password)) {
            // 登录成功,生成Token(框架自带Auth工具)
            $token = $user->createToken('api-token')->plainTextToken;

            return response()->json([
                'status' => 'success',
                'token' => $token
            ], 200);
        }

        // 登录失败
        return response()->json([
            'status' => 'error',
            'msg' => 'Invalid credentials'
        ], 401);
    }
}

看清楚区别了吗?
在原生代码里,你是在“处理一个URL请求”。
在框架代码里,你在“响应一个功能请求”。

优势:

  1. 职责分离: 想改登录逻辑?去 LoginController。想改数据库字段?去 User 模型。想改HTML模板?去 login.blade.php。再也不用在一个文件里翻江倒海了。
  2. 可读性: 代码结构一目了然,新来的实习生(或者半年后的你自己)一看就知道这段代码是干嘛的。

2. 依赖注入(DI):你的代码不再孤独

这是原生PHP开发者最头疼的问题之一:组件之间的耦合

在原生代码里,你想用数据库,你就得 new mysqli()。你想用邮件服务,你就得 new PHPMailer()。如果你的代码里到处都是 new,那么当你想换一个数据库库(比如从MySQL换成PostgreSQL)时,你需要把整个项目里所有的 new 全部删掉改掉。

这就是“紧耦合”。你被绑死在了某个具体的实现上。

框架引入了依赖注入(Dependency Injection)IoC容器。这是一个非常酷的概念,虽然名字听起来很吓人,其实就是一种“自动装配”。

class OrderService
{
    protected $database;
    protected $logger;
    protected $paymentGateway;

    // 构造函数注入:框架会在运行时,自动把好的 $database 和 $logger 扔进这个方法里
    public function __construct(
        DatabaseConnection $database,
        Logger $logger,
        PaymentGateway $paymentGateway
    ) {
        $this->database = $database;
        $this->logger = $logger;
        $this->paymentGateway = $paymentGateway;
    }

    public function createOrder($amount)
    {
        // 框架已经把好用的数据库传给你了,你直接用就行了
        $this->database->beginTransaction();

        try {
            // 业务逻辑...
            $this->paymentGateway->charge($amount);

            $this->database->commit();
            $this->logger->info("Order created with amount: $amount");
            return true;
        } catch (Exception $e) {
            $this->database->rollBack();
            $this->logger->error("Order failed: " . $e->getMessage());
            throw $e;
        }
    }
}

为什么要这么写?

  1. 可测试性: 原生代码里,你很难写测试,因为数据库是真实的,网络是真实的。但有了依赖注入,你可以在测试的时候,注入一个“假的数据库对象”和一个“假的支付网关对象”。这样你就可以在没有任何真实数据的情况下,测试你的业务逻辑是否正确。
  2. 灵活性: 代码里没有 new 了,只有接口(Interface)。如果你明天想换一个支付网关,你只需要修改配置,把 PaymentGateway 的实现换掉,你的 OrderService 一行代码都不用改!

第三部分:ORM与数据库抽象

在原生PHP里,写SQL是家常便饭。而且,如果不小心,你会写出最糟糕的SQL。

1. SQL注入的幽灵

// 原生PHP里的定时炸弹
$id = $_GET['id'];
$sql = "SELECT * FROM products WHERE id = $id";
$result = mysqli_query($conn, $sql);

如果有人访问 /product?id=1 OR 1=1,你的数据库就完了。这是每个PHP老鸟的噩梦。你需要时刻记得加引号、转义、使用预处理语句。

2. 框架的救赎:Eloquent/ORM

框架里的 ORM(对象关系映射)技术,让你在写代码的时候感觉像是在写对象,而不是在写SQL。

// Laravel 的 Eloquent ORM
$product = Product::find($id); // 它帮你自动拼好了 SELECT * FROM products WHERE id = ?

框架内部会处理所有的SQL注入防护和连接池管理。它把枯燥的SQL变成了一行行优雅的PHP代码。

// 复杂查询也不再是噩梦
$users = User::where('age', '>', 18)
            ->whereHas('orders', function($query) {
                $query->where('status', 'completed');
            })
            ->orderBy('created_at', 'desc')
            ->paginate(15);

这就不是简单的 SELECT 了,这是连表查询、条件过滤、分页。在原生代码里,你得写好几个 JOIN 语句,还要处理 mysqli_fetch_array 的循环。而在这里,你只需要链式调用。不仅少写了代码,而且逻辑清晰得像波普画。

第四部分:Composer与生态系统

原生PHP开发还有一个巨大的痛点:管理依赖

以前的PHP项目,要引入一个第三方库,你得去GitHub上下载那个库,然后手动 include 进来。如果这个库还依赖另外两个库,你还得手动下载那两个。如果这两个库又依赖其他的……最后你得到的是一个目录结构乱七八糟的 vendor 文件夹,里面塞满了各种不兼容的版本。

这就是“天下大乱”。

Composer 的出现,简直是PHP的救世主。

它就像是 Node.js 的 npm 或者 Python 的 pip。你只需要在 composer.json 里写一行:

"require": {
    "guzzlehttp/guzzle": "^7.0"
}

然后运行 composer install。框架会自动帮你把 Guzzle 库下载好,并且配置好自动加载器。

为什么转向框架?因为框架本身就是基于 Composer 建立起来的生态圈。

你用 Laravel,不是因为你喜欢那个Logo,而是因为你有几万个现成的软件包可以用:

  • 想做验证码?Intervention Image
  • 想发邮件?Laravel Mail
  • 想写队列任务?Laravel Queue
  • 想做API文档?Laravel Swagger

如果你用原生PHP,写这些功能你得自己造轮子。写个验证码库?你要处理图片裁剪、扭曲、干扰线。写个队列?你要处理数据库锁、进程管理。这不仅是重复造轮子,而且轮子还圆得不好看。

而在框架里,这些东西都是现成的“神装”,你拿起来就能用。

第五部分:自动化测试与工程质量

最后这一点,可能是原生开发者和框架开发者最大的分水岭。

原生PHP开发者往往不相信测试。

为什么?因为写单元测试太难了。你得 mock 所有的东西。如果你用原生代码,写一个测试用例可能需要几百行代码来初始化环境。

框架开发者拥抱测试。

因为有了依赖注入,因为有了容器,框架让写测试变得极其简单。

// 测试登录逻辑
public function test_login_returns_token_with_valid_credentials()
{
    // 1. 准备数据
    $user = User::factory()->create([
        'email' => '[email protected]',
        'password' => bcrypt('password'),
    ]);

    // 2. 模拟请求
    $response = $this->post('/login', [
        'email' => '[email protected]',
        'password' => 'password',
    ]);

    // 3. 断言结果
    $response->assertStatus(200)
             ->assertJson(['token' => true]);

    // 甚至连数据库连接都不需要真实的!
}

写测试能让你拥有巨大的安全感。当你重构代码,或者添加新功能时,你不用怕改坏了旧功能,直接点一下测试按钮,红绿灯告诉你哪里出错了。

这就像是在搭积木,原生开发是拿胶水糊,糊坏了就毁了;框架开发是搭积木,搭错了推倒重来,有说明书,有辅助工具,还能画图规划。

第六部分:团队协作与规范

如果只有你一个人开发,原生PHP确实有它的“自由”。你想怎么写就怎么写,没人管你。

但一旦你要加入团队,或者接手别人的项目,原生PHP就是灾难。

你想想,你接手了一个用了5年的原生PHP项目。
你打开 header.php,发现里面写了 require 'db.php'
你打开 footer.php,发现里面写了 require 'config.php'
你打开 functions.php,发现里面定义了 function connect()
你打开 utils.php,发现里面又定义了 function connect()

好,你有两个 connect() 函数。哪个生效?哪个是旧的?哪个会有Bug?

这就是没有规范的后果。

框架强制你遵守规范(PSR-1, PSR-4)。框架规定了文件结构,规定了类名怎么写,规定了依赖怎么注入。

当你和一个使用框架的团队协作时,你不需要问“这个类放在哪?”。框架会告诉你放在 App/Http/Controllers
你不需要问“这个服务怎么获取?”。容器会告诉你通过构造函数注入。
这种标准化的开发流程,极大地降低了沟通成本。

总结一下(虽然没有总结词,但要点要明)

为什么大家放弃原生开发转向框架?

  1. 为了活命: 原生PHP的 global 变量和面条式代码,会让你的大脑在一个月内老化十岁。框架帮你梳理了逻辑,让你活得久一点。
  2. 为了效率: 别的框架开发者正在用Composer安装一个邮件库,你还在手动解压ZIP包、复制文件、修改 include 路径。框架帮你省下了几万小时的生命。
  3. 为了架构: 原生PHP是代码的堆砌,框架是建筑的蓝图。MVC、依赖注入、ORM,这些不是花架子,它们是你从“码农”进阶为“软件工程师”的阶梯。
  4. 为了自信: 用原生代码提交代码时,你总担心会有Bug;用框架代码提交代码时,看着那一排排测试通过的绿色对勾,你会觉得世界充满了爱与和平。

当然,承认框架很重要,并不代表我们要完全抛弃原生PHP的某些技巧。有时候,在一个极其轻量的脚本里,原生PHP依然很快。但对于绝大多数的中大型项目来说,原生PHP就像是在裸奔。

所以,朋友们,如果你还在写那种几千行一个文件、全是 requireglobal 的原生代码,听我一句劝:赶紧去学个框架吧。别让你的代码成为那个深夜里让你痛哭流涕的“屎山”。

好了,今天的讲座就到这里。现在,让我们放下原生代码,去拥抱那个代码整洁、架构清晰、充满希望的PHP框架世界吧!

(鞠躬,下台)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注