|
直接读源码是目实码掌握PHP最快的方式,没有之一。战案我见过太多人抱着《PHP从入门到精通》啃了三个月,例源连一个登录功能都写不出来。目实码问题出在哪?战案光看不练。今天拿一个实际项目——简易博客系统——把核心代码拆开讲,例源你能直接复制到本地跑起来。目实码 项目结构不复杂,战案五个文件搞定:config.php、例源index.php、目实码post.php、战案admin.php、例源style.css。目实码数据库用MySQL,战案建一张表articles,例源字段就四个:id、title、content、created_at。别搞太花哨,先把增删改查跑通。 先看config.php,这是入口: <?php $host = 'localhost'; $db = 'blog'; $user = 'root'; $pass = '123456'; $charset = 'utf8mb4'; $dsn = "mysql:host=$host;dbname=$db;charset=$charset"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new PDO($dsn, $user, $pass, $options); } catch (PDOException $e) { throw new PDOException($e->getMessage(), (int)$e->getCode()); } ?> 注意PDO::ATTR_EMULATE_PREPARES => false这行。很多人忽略它,结果预处理语句在MySQL里还是被模拟执行,SQL注入照样能钻空子。关掉模拟,让MySQL真正做预处理,安全级别立刻不一样。 index.php负责列出所有文章。核心就一句查询: $stmt = $pdo->query("SELECT id, title, created_at FROM articles ORDER BY created_at DESC"); $articles = $stmt->fetchAll(); 然后循环输出。这里有个细节:created_at建议用datetime类型,别用时间戳。为什么?直接读数据库的时候,2025-03-21 14:30:00比1742567400直观得多,排查问题省心。 文章详情页post.php接收?id=参数。这里必须做整数过滤: $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); if (!$id) { die('文章不存在'); } $stmt = $pdo->prepare("SELECT * FROM articles WHERE id = ?"); $stmt->execute([$id]); $post = $stmt->fetch(); 用filter_input比intval更严谨,非整数直接返回false。prepare加占位符,杜绝拼接。这两步做完,基本的注入风险就堵死了。 后台admin.php稍微复杂点,包含发布和删除。发布逻辑: if ($_SERVER['REQUEST_METHOD'] === 'POST') { $title = trim($_POST['title'] ?? ''); $content = trim($_POST['content'] ?? ''); if ($title && $content) { $stmt = $pdo->prepare("INSERT INTO articles (title, content, created_at) VALUES (?, ?, NOW())"); $stmt->execute([$title, $content]); header('Location: index.php'); exit; } } 删除用GET传id不太规范,但小项目图省事可以接受。正式环境应该用POST加CSRF token。我实际部署时踩过坑:没加token,被人用 这种标签把文章全删了。浏览器加载图片时自动发了GET请求,防不胜防。 前端模板直接混写PHP,不推荐用框架的时候这么干,但学习阶段这样最直观。style.css随便写几行,让页面能看就行。整个项目跑起来不到200行代码,但涵盖了数据库连接、预处理查询、参数过滤、重定向、基本CRUD。比看十篇理论文章管用。 源码我放在GitHub上了,搜php-mini-blog就能找到。建议你先把代码敲一遍,然后故意改错几个地方——比如把prepare换成直接拼接,试试用' OR '1'='1能不能注入进去。亲眼看到漏洞怎么产生,比背一百条安全规则都记得牢。 遇到fetchAll()返回空数组别慌,先检查PDO::ATTR_DEFAULT_FETCH_MODE有没有设成FETCH_ASSOC。默认是FETCH_BOTH,结果里同时有数字索引和字段名,打印出来一堆重复数据,容易看晕。这个坑我踩过三次。 |