<返回目录     Powered by claud/xia兄

第15课: 框架与最佳实践

流行的PHP框架

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";
?>

代码级优化技术

数据库性能优化

<?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优化配置

[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';
?>

安全配置最佳实践

安全工具与扫描

# 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;
    }
}
?>

性能提升

性能优化

恭喜! 你已完成PHP基础课程。继续学习Laravel等框架,开始构建实际项目吧!