各位朋友,大家晚上好!
欢迎大家来到我的讲座现场。先问大家一个问题:你们现在写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管理全塞在一个文件里。
痛点在于:
- 重复造轮子: 每次都要手动
mysqli_connect,每次都要判断if (strpos...)。 - 没有命名空间: 几个文件之后,你的
$user变量可能是一个对象,也可能是一个数组,全靠脑子记。 - 难以测试: 你想测试
login函数?行啊,你得把整个数据库、Session、HTTP请求都模拟出来,或者写个脚本手动$_POST数据。但在原生代码里,函数是紧紧耦合在全局变量上的,你根本无法单独测试它。 - “面条式代码”: 这种代码结构就像面条一样,没有形状,你不知道哪头是头,哪头是尾。
当你接到一个需求:“老板,把这个登录页面的密码强度验证加一下。” 你得去哪加?去那个几万行的 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请求”。
在框架代码里,你在“响应一个功能请求”。
优势:
- 职责分离: 想改登录逻辑?去
LoginController。想改数据库字段?去User模型。想改HTML模板?去login.blade.php。再也不用在一个文件里翻江倒海了。 - 可读性: 代码结构一目了然,新来的实习生(或者半年后的你自己)一看就知道这段代码是干嘛的。
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;
}
}
}
为什么要这么写?
- 可测试性: 原生代码里,你很难写测试,因为数据库是真实的,网络是真实的。但有了依赖注入,你可以在测试的时候,注入一个“假的数据库对象”和一个“假的支付网关对象”。这样你就可以在没有任何真实数据的情况下,测试你的业务逻辑是否正确。
- 灵活性: 代码里没有
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。
你不需要问“这个服务怎么获取?”。容器会告诉你通过构造函数注入。
这种标准化的开发流程,极大地降低了沟通成本。
总结一下(虽然没有总结词,但要点要明)
为什么大家放弃原生开发转向框架?
- 为了活命: 原生PHP的
global变量和面条式代码,会让你的大脑在一个月内老化十岁。框架帮你梳理了逻辑,让你活得久一点。 - 为了效率: 别的框架开发者正在用Composer安装一个邮件库,你还在手动解压ZIP包、复制文件、修改
include路径。框架帮你省下了几万小时的生命。 - 为了架构: 原生PHP是代码的堆砌,框架是建筑的蓝图。MVC、依赖注入、ORM,这些不是花架子,它们是你从“码农”进阶为“软件工程师”的阶梯。
- 为了自信: 用原生代码提交代码时,你总担心会有Bug;用框架代码提交代码时,看着那一排排测试通过的绿色对勾,你会觉得世界充满了爱与和平。
当然,承认框架很重要,并不代表我们要完全抛弃原生PHP的某些技巧。有时候,在一个极其轻量的脚本里,原生PHP依然很快。但对于绝大多数的中大型项目来说,原生PHP就像是在裸奔。
所以,朋友们,如果你还在写那种几千行一个文件、全是 require 和 global 的原生代码,听我一句劝:赶紧去学个框架吧。别让你的代码成为那个深夜里让你痛哭流涕的“屎山”。
好了,今天的讲座就到这里。现在,让我们放下原生代码,去拥抱那个代码整洁、架构清晰、充满希望的PHP框架世界吧!
(鞠躬,下台)