第15课: 框架与最佳实践
流行的PHP框架
- Laravel - 最流行的现代PHP框架
- Symfony - 企业级框架
- CodeIgniter - 轻量级框架
- Yii - 高性能框架
- Slim - 微框架
Composer包管理
# 安装Composer
curl -sS https://getcomposer.org/installer | php
# 初始化项目
composer init
# 安装依赖
composer require vendor/package
# 自动加载
composer dump-autoload
代码组织最佳实践
project/
├── src/
│ ├── Controllers/ # 控制器
│ ├── Models/ # 模型
│ ├── Views/ # 视图
│ ├── Services/ # 业务逻辑
│ ├── Repositories/ # 数据访问
│ ├── Middleware/ # 中间件
│ ├── Traits/ # 特性
│ └── Exceptions/ # 异常处理
├── public/
│ └── index.php # 入口文件
├── config/ # 配置文件
├── database/ # 数据库迁移和种子
├── resources/ # 资源文件
├── storage/ # 存储文件
├── vendor/ # 依赖包
└── composer.json # Composer配置
设计模式
创建型模式
<?php
// 单例模式
class Database {
private static $instance;
private function __construct() {}
private function __clone() {}
private function __wakeup() {}
public static function getInstance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function connect() {
// 数据库连接逻辑
}
}
// 工厂模式
interface Logger {
public function log($message);
}
class FileLogger implements Logger {
public function log($message) {
file_put_contents('log.txt', $message . "\n", FILE_APPEND);
}
}
class DatabaseLogger implements Logger {
public function log($message) {
// 数据库日志记录
}
}
class LoggerFactory {
public static function create($type) {
switch ($type) {
case 'file':
return new FileLogger();
case 'database':
return new DatabaseLogger();
default:
throw new Exception('Invalid logger type');
}
}
}
?>
结构型模式
<?php
// 适配器模式
interface PaymentGateway {
public function pay($amount);
}
class PayPalGateway implements PaymentGateway {
public function pay($amount) {
// PayPal支付逻辑
echo "Paying $amount via PayPal\n";
}
}
class StripeGateway implements PaymentGateway {
public function pay($amount) {
// Stripe支付逻辑
echo "Paying $amount via Stripe\n";
}
}
// 装饰器模式
class BasicUser {
public function getPermissions() {
return ['read'];
}
}
class AdminDecorator {
private $user;
public function __construct($user) {
$this->user = $user;
}
public function getPermissions() {
return array_merge($this->user->getPermissions(), ['write', 'delete']);
}
}
?>
行为型模式
<?php
// 观察者模式
interface Observer {
public function update($subject);
}
class Subject {
private $observers = [];
public function attach(Observer $observer) {
$this->observers[] = $observer;
}
public function detach(Observer $observer) {
$this->observers = array_filter($this->observers, function($o) use ($observer) {
return $o !== $observer;
});
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
}
class EmailObserver implements Observer {
public function update($subject) {
// 发送邮件通知
}
}
class SmsObserver implements Observer {
public function update($subject) {
// 发送短信通知
}
}
// 策略模式
interface SortStrategy {
public function sort(array $data);
}
class BubbleSort implements SortStrategy {
public function sort(array $data) {
// 冒泡排序
return $data;
}
}
class QuickSort implements SortStrategy {
public function sort(array $data) {
// 快速排序
return $data;
}
}
class Sorter {
private $strategy;
public function setStrategy(SortStrategy $strategy) {
$this->strategy = $strategy;
}
public function sort(array $data) {
return $this->strategy->sort($data);
}
}
?>
性能调优专题
性能分析工具
<?php
// 使用Xdebug进行性能分析
// 在php.ini中启用
/*
zend_extension=xdebug.so
xdebug.mode=profile
xdebug.output_dir=/path/to/profile
*/
// 使用PHP内置的性能分析
function benchmark($callback, $iterations = 10000) {
$start = microtime(true);
$memory = memory_get_usage();
for ($i = 0; $i < $iterations; $i++) {
$callback();
}
$end = microtime(true);
$memoryEnd = memory_get_usage();
return [
'time' => ($end - $start) * 1000, // 毫秒
'memory' => ($memoryEnd - $memory) / 1024 // KB
];
}
// 使用示例
$result = benchmark(function() {
$array = [];
for ($i = 0; $i < 1000; $i++) {
$array[] = $i;
}
});
echo "Time: {$result['time']}ms, Memory: {$result['memory']}KB";
?>
代码级优化技术
- 使用适当的数据结构:根据场景选择数组、对象或Spl数据结构
- 避免重复计算:使用缓存存储计算结果
- 减少函数调用:避免在循环中进行频繁的函数调用
- 使用引用传递:对于大数组,使用引用传递减少内存复制
- 避免使用eval():eval()函数执行效率低且不安全
- 使用类型声明:PHP 7+支持的类型声明可以提高执行效率
- 优化循环结构:减少循环内的操作,使用更高效的循环类型
- 使用生成器:对于大数据集,使用生成器减少内存使用
- 避免使用魔术方法:__get、__set等魔术方法执行效率较低
- 使用静态方法:对于无状态操作,静态方法执行更快
数据库性能优化
<?php
// 1. 使用索引
// CREATE INDEX idx_users_email ON users(email);
// 2. 减少查询次数 - 使用连接查询
// 避免N+1查询问题
$users = $pdo->query("SELECT * FROM users WHERE active = 1")->fetchAll();
foreach ($users as $user) {
// 避免在循环中查询
//$posts = $pdo->query("SELECT * FROM posts WHERE user_id = {$user['id']}")->fetchAll();
}
// 正确的做法:使用连接查询
$stmt = $pdo->prepare("SELECT u.*, p.* FROM users u LEFT JOIN posts p ON u.id = p.user_id WHERE u.active = 1");
$stmt->execute();
$results = $stmt->fetchAll();
// 3. 使用预处理语句
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(['email' => $email]);
// 4. 限制结果集大小
$stmt = $pdo->prepare("SELECT * FROM users LIMIT :limit OFFSET :offset");
$stmt->execute(['limit' => 10, 'offset' => 0]);
// 5. 使用缓存
$cacheKey = "users_" . md5($query);
if (isset($cache[$cacheKey])) {
return $cache[$cacheKey];
}
// 执行查询...
$cache[$cacheKey] = $result;
?>
缓存策略
<?php
// 1. 内存缓存 - Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 设置缓存
$redis->set('user:1', json_encode($user), 3600); // 1小时过期
// 获取缓存
$userData = $redis->get('user:1');
if ($userData) {
$user = json_decode($userData, true);
}
// 2. 文件缓存
function cache_get($key, $ttl = 3600) {
$cacheFile = __DIR__ . '/cache/' . md5($key) . '.php';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $ttl)) {
return include $cacheFile;
}
return false;
}
function cache_set($key, $data) {
$cacheFile = __DIR__ . '/cache/' . md5($key) . '.php';
file_put_contents($cacheFile, '<?php return ' . var_export($data, true) . ';');
}
// 3. 页面缓存
ob_start();
// 生成页面内容
$content = ob_get_clean();
cache_set('page:home', $content);
?>
服务器优化
- 启用OPcache:显著提高PHP执行速度
- 使用PHP-FPM:比CGI模式更高效
- 配置适当的内存限制:根据应用需求设置memory_limit
- 启用Gzip压缩:减少传输数据量
- 使用CDN:加速静态资源加载
- 配置适当的连接池:减少数据库连接开销
- 使用HTTP/2:支持多路复用,减少连接开销
- 优化TCP参数:调整内核参数提高网络性能
- 使用负载均衡:分散流量,提高可用性
OPcache优化配置
[opcache]
zend_extension=opcache.so
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.validate_timestamps=1
opcache.save_comments=1
opcache.fast_shutdown=1
opcache.jit_buffer_size=100M
opcache.jit=tracing
性能测试与基准测试
# 使用ab进行HTTP基准测试
ab -n 1000 -c 100 http://localhost/test.php
# 使用wrk进行更高级的测试
wrk -t12 -c400 -d30s http://localhost/test.php
# PHP内置基准测试
php -r '$start = microtime(true); for ($i=0; $i<1000000; $i++) { $a = $i * $i; } echo microtime(true) - $start;'
# 使用XHProf进行性能分析
git clone https://github.com/phacility/xhprof.git
cd xhprof/extension
phpize
./configure
make && make install
# 在php.ini中启用
[xhprof]
extension=xhprof.so
xhprof.output_dir=/tmp/xhprof
安全编程专题
输入验证与过滤
<?php
// 1. 基本输入验证
function validateInput($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
// 2. 使用filter_var进行复杂验证
$email = "user@example.com";
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Invalid email format";
}
$url = "https://example.com";
if (!filter_var($url, FILTER_VALIDATE_URL)) {
echo "Invalid URL format";
}
// 3. 使用filter_input直接从输入源获取并验证
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false) {
echo "Invalid ID";
}
// 4. 使用过滤规则
$options = [
'options' => [
'min_range' => 1,
'max_range' => 1000
]
];
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, $options);
// 5. 自定义验证函数
function validatePassword($password) {
if (strlen($password) < 8) {
return "密码长度至少8位";
}
if (!preg_match('/[A-Z]/', $password)) {
return "密码必须包含大写字母";
}
if (!preg_match('/[0-9]/', $password)) {
return "密码必须包含数字";
}
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
return "密码必须包含特殊字符";
}
return true;
}
?>
SQL注入防护
<?php
// 1. 使用PDO预处理语句(推荐)
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// 准备语句
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND status = :status");
// 绑定参数并执行
$stmt->execute([
'email' => $email,
'status' => $status
]);
$user = $stmt->fetch();
} catch (PDOException $e) {
echo "Database error: " . $e->getMessage();
}
// 2. 使用mysqli预处理语句
$mysqli = new mysqli('localhost', 'username', 'password', 'test');
if ($mysqli->connect_error) {
die("Connection failed: " . $mysqli->connect_error);
}
$stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
// 3. 避免危险的SQL函数
// 不安全的做法
// $sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
// 4. 使用ORM框架
// 使用Laravel Eloquent
// $user = User::where('email', $email)->first();
?>
密码安全
<?php
// 1. 安全存储密码
$password = "user_password";
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 2. 验证密码
if (password_verify($password, $hashedPassword)) {
echo "Password is valid";
} else {
echo "Invalid password";
}
// 3. 定期重新哈希密码
if (password_needs_rehash($hashedPassword, PASSWORD_DEFAULT)) {
$newHashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 更新数据库中的密码
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
$stmt->execute([$newHashedPassword, $userId]);
}
// 4. 密码策略实施
function enforcePasswordPolicy($password) {
$errors = [];
if (strlen($password) < 12) {
$errors[] = "密码长度至少12位";
}
if (!preg_match('/[A-Z]/', $password)) {
$errors[] = "密码必须包含大写字母";
}
if (!preg_match('/[a-z]/', $password)) {
$errors[] = "密码必须包含小写字母";
}
if (!preg_match('/[0-9]/', $password)) {
$errors[] = "密码必须包含数字";
}
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
$errors[] = "密码必须包含特殊字符";
}
return $errors;
}
// 5. 防止暴力破解
function loginAttempts($username) {
$key = "login_attempts_" . $username;
$attempts = $_SESSION[$key] ?? 0;
if ($attempts >= 5) {
return "Too many login attempts. Please try again later.";
}
// 验证失败时增加计数
$_SESSION[$key] = $attempts + 1;
return null;
}
?>
跨站脚本(XSS)防护
<?php
// 1. 输出转义
$userInput = "<script>alert('XSS')</script>";
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// 2. 使用Content-Security-Policy
// 在HTTP头中设置
// header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com");
// 或在HTML中设置
/*
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
*/
// 3. 过滤危险标签和属性
function sanitizeHTML($input) {
$allowedTags = '<p><br><strong><em><u>';
return strip_tags($input, $allowedTags);
}
// 4. 使用HTML Purifier库
/*
require_once 'HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html);
*/
?>
跨站请求伪造(CSRF)防护
<?php
// 1. 生成CSRF令牌
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// 2. 在表单中包含CSRF令牌
/*
<form method="post" action="submit.php">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<!-- 其他表单字段 -->
<button type="submit">Submit</button>
</form>
*/
// 3. 验证CSRF令牌
function validateCSRFToken() {
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF token validation failed");
}
}
// 在处理表单提交时调用
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
validateCSRFToken();
// 处理表单数据
}
// 4. 使用SameSite cookie属性
session_set_cookie_params([
'lifetime' => 86400,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
?>
安全的会话管理
<?php
// 1. 安全的会话配置
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.use_only_cookies', 1);
ini_set('session.entropy_length', 32);
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.hash_function', 'sha256');
ini_set('session.hash_bits_per_character', 6);
// 2. 会话启动前的配置
session_set_cookie_params([
'lifetime' => 3600, // 1小时
'path' => '/',
'domain' => '',
'secure' => true, // 只在HTTPS下传输
'httponly' => true, // 防止JavaScript访问
'samesite' => 'Strict' // 防止CSRF攻击
]);
session_start();
// 3. 会话固定攻击防护
function regenerateSession() {
// 保存当前会话数据
$sessionData = $_SESSION;
// 结束当前会话
session_destroy();
// 启动新会话
session_start();
session_regenerate_id(true);
// 恢复会话数据
$_SESSION = $sessionData;
}
// 登录成功后调用
if (loginSuccess()) {
regenerateSession();
$_SESSION['user_id'] = $userId;
}
// 4. 会话超时处理
function checkSessionTimeout() {
$timeout = 1800; // 30分钟
if (isset($_SESSION['last_activity'])) {
$elapsed = time() - $_SESSION['last_activity'];
if ($elapsed > $timeout) {
session_unset();
session_destroy();
header('Location: login.php');
exit;
}
}
$_SESSION['last_activity'] = time();
}
// 在每个受保护的页面调用
checkSessionTimeout();
?>
文件上传安全
<?php
// 1. 安全的文件上传处理
function uploadFile($file, $uploadDir) {
$errors = [];
$maxSize = 2 * 1024 * 1024; // 2MB
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
// 检查错误
if ($file['error'] !== UPLOAD_ERR_OK) {
$errors[] = "上传错误: " . $file['error'];
return $errors;
}
// 检查文件大小
if ($file['size'] > $maxSize) {
$errors[] = "文件大小超过限制";
}
// 检查文件类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, $allowedTypes)) {
$errors[] = "不允许的文件类型";
}
// 检查文件扩展名
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) {
$errors[] = "不允许的文件扩展名";
}
// 生成唯一文件名
$fileName = uniqid() . '.' . $extension;
$targetPath = $uploadDir . '/' . $fileName;
// 移动文件
if (empty($errors)) {
if (!move_uploaded_file($file['tmp_name'], $targetPath)) {
$errors[] = "文件移动失败";
}
}
return empty($errors) ? $fileName : $errors;
}
// 2. 防止文件路径遍历攻击
function sanitizeFileName($fileName) {
// 移除路径部分
$fileName = basename($fileName);
// 移除特殊字符
$fileName = preg_replace('/[^a-zA-Z0-9._-]/', '', $fileName);
return $fileName;
}
// 3. 存储文件路径而非实际文件
// 将文件存储在非web可访问目录
// $uploadDir = __DIR__ . '/uploads';
?>
安全配置最佳实践
- 使用HTTPS:保护数据传输安全
- 设置适当的文件权限:避免敏感文件被访问(推荐644用于文件,755用于目录)
- 禁用危险的PHP函数:在php.ini中设置disable_functions
- 隐藏PHP版本:在php.ini中设置expose_php = Off
- 使用安全的错误处理:生产环境中不显示详细错误信息
- 定期更新PHP和依赖包:及时修复安全漏洞
- 使用Web应用防火墙(WAF):如ModSecurity
- 实施最小权限原则:数据库用户只授予必要的权限
- 加密敏感数据:使用openssl加密存储敏感信息
- 进行安全审计:定期扫描安全漏洞
安全工具与扫描
# 1. 使用PHP Security Scanner
composer require --dev sensiolabs/security-checker
php vendor/bin/security-checker security:check
# 2. 使用OWASP ZAP进行安全扫描
# https://www.zaproxy.org/download/
# 3. 使用SQLmap进行SQL注入测试
# https://sqlmap.org/
# 4. 使用Nikto进行Web服务器扫描
nikto -h example.com
# 5. 代码审计工具
# PHP CodeSniffer with security rules
composer require --dev squizlabs/php_codesniffer
composer require --dev phpcompatibility/php-compatibility
# 6. 安全 headers
# 使用Helmet.js或手动设置安全头
/*
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("X-XSS-Protection: 1; mode=block");
header("Referrer-Policy: strict-origin-when-cross-origin");
*/
密码处理
<?php
// 加密密码
$hash = password_hash($password, PASSWORD_DEFAULT);
// 验证密码
if (password_verify($password, $hash)) {
echo "密码正确";
}
?>
错误处理
<?php
// 开发环境
ini_set('display_errors', 1);
error_reporting(E_ALL);
// 生产环境
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/error.log');
// 自定义错误处理
set_error_handler(function($errno, $errstr, $errfile, $errline) {
error_log("Error: $errstr in $errfile:$errline");
});
?>
PHP 8新特性
命名参数
<?php
// PHP 7 及之前
function createUser($name, $email, $admin = false) {
// 函数逻辑
}
createUser('John Doe', 'john@example.com', true);
// PHP 8
function createUser($name, $email, $admin = false) {
// 函数逻辑
}
createUser(
name: 'John Doe',
email: 'john@example.com',
admin: true
);
?>
联合类型
<?php
// PHP 7 及之前
/**
* @param string|int $id
* @return string|array
*/
function processId($id) {
// 函数逻辑
}
// PHP 8
function processId(string|int $id): string|array {
if (is_int($id)) {
return "ID: {$id}";
}
return ['id' => $id];
}
?>
Match表达式
<?php
// PHP 7 及之前
switch ($status) {
case 'active':
$message = 'User is active';
break;
case 'inactive':
$message = 'User is inactive';
break;
default:
$message = 'Unknown status';
}
// PHP 8
$message = match ($status) {
'active' => 'User is active',
'inactive' => 'User is inactive',
default => 'Unknown status',
};
// 支持表达式匹配
$result = match (true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
default => 'D',
};
?>
构造函数属性提升
<?php
// PHP 7 及之前
class User {
private string $name;
private string $email;
private bool $admin;
public function __construct(string $name, string $email, bool $admin = false) {
$this->name = $name;
$this->email = $email;
$this->admin = $admin;
}
}
// PHP 8
class User {
public function __construct(
private string $name,
private string $email,
private bool $admin = false
) {}
}
?>
空安全运算符
<?php
// PHP 7 及之前
$username = null;
if ($user !== null) {
if ($user->profile !== null) {
$username = $user->profile->username;
}
}
// PHP 8
$username = $user?->profile?->username;
// 结合空合并运算符
$username = $user?->profile?->username ?? 'Guest';
?>
静态返回类型
<?php
// PHP 8
class User {
public static function create(): static {
return new static();
}
}
class Admin extends User {
// 继承的create()方法会返回Admin实例
}
$admin = Admin::create(); // 返回Admin实例
?>
注解
<?php
// PHP 8
#[Attribute]
class Route {
public function __construct(private string $path, private string $method = 'GET') {}
}
class UserController {
#[Route('/users', 'GET')]
public function index() {
// 处理请求
}
#[Route('/users/{id}', 'GET')]
public function show(int $id) {
// 处理请求
}
}
?>
更强大的类型系统
<?php
// PHP 8
// 原生类型
function sum(int ...$numbers): int {
return array_sum($numbers);
}
// 泛型(通过扩展实现)
class Collection {
/** @var array<T> */
private array $items;
/** @param array<T> $items */
public function __construct(array $items) {
$this->items = $items;
}
/** @return array<T> */
public function all(): array {
return $this->items;
}
}
?>
性能提升
- JIT编译器: 即时编译,提升执行速度
- 类型系统优化: 减少运行时类型检查开销
- 内存使用优化: 减少内存占用
- 核心函数性能提升: 许多内置函数执行更快
性能优化
- 使用OPcache缓存字节码
- 实现数据缓存(Redis、Memcached)
- 优化数据库查询
- 使用CDN加速静态资源
- 启用Gzip压缩
恭喜! 你已完成PHP基础课程。继续学习Laravel等框架,开始构建实际项目吧!