本文目录导读:

- 什么是 PHP Boilerplate
- 基本项目结构
- 基础 Boilerplate 示例
- 配置文件 (config.php)
- .htaccess 配置
- 模型基类 (Model.php)
- 使用 Composer 的 Boilerplate
- 模板引擎集成 (可选)
- 安全加固
- 快速开始脚本
- 最佳实践
我来详细介绍 PHP Boilerplate(样板代码/脚手架)的创建和使用方法。
什么是 PHP Boilerplate
Boilerplate 是预先写好的代码模板,可以帮助你快速启动新项目,避免重复编写基础代码。
基本项目结构
project/
├── public/ # 公开访问目录
│ ├── index.php # 入口文件
│ ├── css/ # 样式文件
│ └── js/ # JavaScript文件
├── src/ # 源代码
│ ├── Core/ # 核心类
│ ├── Models/ # 数据模型
│ ├── Views/ # 视图文件
│ └── Controllers/ # 控制器
├── config/ # 配置文件
├── vendor/ # 依赖包
└── .htaccess # Apache配置
基础 Boilerplate 示例
1 入口文件 (index.php)
<?php
declare(strict_types=1);
/**
* PHP Boilerplate 入口文件
*/
// 1. 定义常量
define('ROOT_PATH', __DIR__ . '/../');
define('APP_PATH', ROOT_PATH . 'src/');
define('VIEW_PATH', APP_PATH . 'Views/');
// 2. 加载配置
require_once ROOT_PATH . 'config/config.php';
// 3. 自动加载类
spl_autoload_register(function ($className) {
$prefix = 'App\\';
$baseDir = APP_PATH;
// 检查类名前缀
$len = strlen($prefix);
if (strncmp($prefix, $className, $len) !== 0) {
return;
}
// 获取相对类名并转换路径
$relativeClass = substr($className, $len);
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
// 加载文件
if (file_exists($file)) {
require $file;
}
});
// 4. 错误处理
error_reporting(E_ALL);
ini_set('display_errors', '1');
// 5. 启动应用
use App\Core\Router;
use App\Core\Request;
use App\Core\Response;
$router = new Router();
$request = new Request();
$response = new Response();
// 定义路由
$router->get('/', 'HomeController@index');
$router->get('/users', 'UserController@index');
$router->post('/users', 'UserController@create');
// 运行应用
try {
$router->dispatch($request, $response);
} catch (Exception $e) {
$response->setStatusCode(500);
echo "Error: " . $e->getMessage();
}
2 路由类 (Router.php)
<?php
namespace App\Core;
/**
* 简单路由器
*/
class Router
{
private array $routes = [
'GET' => [],
'POST' => [],
'PUT' => [],
'DELETE' => []
];
public function get(string $path, string $handler): void
{
$this->addRoute('GET', $path, $handler);
}
public function post(string $path, string $handler): void
{
$this->addRoute('POST', $path, $handler);
}
public function put(string $path, string $handler): void
{
$this->addRoute('PUT', $path, $handler);
}
public function delete(string $path, string $handler): void
{
$this->addRoute('DELETE', $path, $handler);
}
private function addRoute(string $method, string $path, string $handler): void
{
$this->routes[$method][$path] = $handler;
}
public function dispatch(Request $request, Response $response): void
{
$method = $request->getMethod();
$uri = $request->getUri();
// 查找路由
if (isset($this->routes[$method][$uri])) {
$handler = $this->routes[$method][$uri];
[$controller, $action] = explode('@', $handler);
$controllerClass = 'App\\Controllers\\' . $controller;
if (class_exists($controllerClass)) {
$controllerObj = new $controllerClass();
if (method_exists($controllerObj, $action)) {
$controllerObj->{$action}($request, $response);
return;
}
}
}
// 404处理
$response->setStatusCode(404);
$response->setContent('Page not found');
$response->send();
}
}
3 请求和响应类
<?php
namespace App\Core;
/**
* 请求处理类
*/
class Request
{
public function getMethod(): string
{
return $_SERVER['REQUEST_METHOD'];
}
public function getUri(): string
{
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
return rtrim($uri, '/') ?: '/';
}
public function getPost(string $key = null)
{
if ($key === null) {
return $_POST;
}
return $_POST[$key] ?? null;
}
public function getQuery(string $key = null)
{
if ($key === null) {
return $_GET;
}
return $_GET[$key] ?? null;
}
public function getJson()
{
return json_decode(file_get_contents('php://input'), true) ?? [];
}
}
/**
* 响应处理类
*/
class Response
{
private int $statusCode = 200;
private string $content = '';
private array $headers = [];
public function setStatusCode(int $code): void
{
$this->statusCode = $code;
}
public function setContent(string $content): void
{
$this->content = $content;
}
public function setHeader(string $name, string $value): void
{
$this->headers[$name] = $value;
}
public function json(mixed $data): void
{
$this->setHeader('Content-Type', 'application/json');
$this->content = json_encode($data);
}
public function send(): void
{
http_response_code($this->statusCode);
foreach ($this->headers as $name => $value) {
header("$name: $value");
}
echo $this->content;
}
}
配置文件 (config.php)
<?php
/**
* 配置文件
*/
return [
'app' => [
'name' => 'My PHP Application',
'env' => 'development', // development, production
'debug' => true,
'url' => 'http://localhost',
],
'database' => [
'host' => 'localhost',
'port' => 3306,
'database' => 'myapp',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
],
'security' => [
'secret_key' => 'your-secret-key-here',
'token_expire' => 3600, // 1 hour
],
'paths' => [
'logs' => ROOT_PATH . 'logs/',
'uploads' => ROOT_PATH . 'public/uploads/',
]
];
.htaccess 配置
Options -Indexes
DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Force HTTPS
# RewriteCond %{HTTPS} !=on
# RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Remove trailing slashes
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Route all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>
# Security headers
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
</IfModule>
# Disable directory browsing
Options -Indexes
# Block access to sensitive files
<FilesMatch "(\.(htaccess|htpasswd|ini|log|sh|inc|bak)$|config\.json|composer\.json)">
Order allow,deny
Deny from all
</FilesMatch>
模型基类 (Model.php)
<?php
namespace App\Core;
use PDO;
use PDOException;
/**
* 数据库模型基类
*/
abstract class Model
{
protected PDO $connection;
protected string $table;
protected string $primaryKey = 'id';
public function __construct()
{
$this->connect();
}
private function connect(): void
{
$config = require ROOT_PATH . 'config/config.php';
$dbConfig = $config['database'];
try {
$dsn = "mysql:host={$dbConfig['host']};dbname={$dbConfig['database']};charset={$dbConfig['charset']}";
$this->connection = new PDO(
$dsn,
$dbConfig['username'],
$dbConfig['password'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
} catch (PDOException $e) {
throw new PDOException("Database connection failed: " . $e->getMessage());
}
}
public function findAll(): array
{
$stmt = $this->connection->prepare("SELECT * FROM {$this->table}");
$stmt->execute();
return $stmt->fetchAll();
}
public function findById(int $id)
{
$stmt = $this->connection->prepare("SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
public function create(array $data): int
{
$columns = implode(', ', array_keys($data));
$placeholders = ':' . implode(', :', array_keys($data));
$sql = "INSERT INTO {$this->table} ({$columns}) VALUES ({$placeholders})";
$stmt = $this->connection->prepare($sql);
foreach ($data as $key => $value) {
$stmt->bindValue(":$key", $value);
}
$stmt->execute();
return (int)$this->connection->lastInsertId();
}
public function update(int $id, array $data): bool
{
$fields = [];
foreach ($data as $key => $value) {
$fields[] = "$key = :$key";
}
$sql = "UPDATE {$this->table} SET " . implode(', ', $fields) . " WHERE {$this->primaryKey} = :id";
$stmt = $this->connection->prepare($sql);
$stmt->bindValue(':id', $id);
foreach ($data as $key => $value) {
$stmt->bindValue(":$key", $value);
}
return $stmt->execute();
}
public function delete(int $id): bool
{
$stmt = $this->connection->prepare("DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?");
return $stmt->execute([$id]);
}
}
使用 Composer 的 Boilerplate
如果你想使用现成的 Boilerplate,推荐以下方案:
1 使用 Laravel
composer create-project --prefer-dist laravel/laravel my-app
2 使用 Symfony Skeleton
composer create-project symfony/skeleton my-app
3 使用 Slim Framework
composer create-project slim/slim-skeleton my-app
模板引擎集成 (可选)
<?php
// 使用 Twig 模板引擎
require_once 'vendor/autoload.php';
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
$loader = new FilesystemLoader(VIEW_PATH);
$twig = new Environment($loader, [
'cache' => ROOT_PATH . 'cache/twig/',
'debug' => true,
]);
// 在你的控制器中使用
$twig->render('home/index.html', ['name' => 'World']);
安全加固
<?php
// SecurityHelper.php
namespace App\Helpers;
class Security
{
public static function sanitize(string $data): string
{
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
public static function validateEmail(string $email): bool
{
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
public static function generateCsrfToken(): string
{
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
public static function validateCsrfToken(): bool
{
$token = $_POST['csrf_token'] ?? '';
return isset($_SESSION['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $token);
}
}
快速开始脚本
创建 create_project.php:
<?php
/**
* 快速创建项目结构的脚本
*/
$projectName = readline("Project name: ");
$projectPath = getcwd() . '/' . $projectName;
// 创建目录结构
$dirs = [
'public/css',
'public/js',
'public/uploads',
'src/Core',
'src/Models',
'src/Views',
'src/Controllers',
'src/Helpers',
'config',
'logs',
'cache',
];
foreach ($dirs as $dir) {
if (!file_exists("$projectPath/$dir")) {
mkdir("$projectPath/$dir", 0777, true);
}
}
echo "Project $projectName created successfully!\n";
最佳实践
-
保持简单:不要过度设计,从小开始
-
使用命名空间:组织代码结构
-
遵循 PSR 标准:PSR-4自动加载,PSR-12编码规范
-
分离关注点:MVC模式
-
配置管理:环境配置文件分离
-
错误处理:开发和生产环境不同的错误处理
-
安全考虑:SQL注入、XSS攻击防护
-
性能优化:使用OPcache缓存,合理使用数据库
这个基础 Boilerplate 可以帮助你快速开始新项目,根据实际需求可以选择使用现有的框架(如 Laravel)或在此基础上扩展,记住要从简单开始,逐步添加功能。