Skip to content

程序结构

概述

了解如何组织PHP程序对于编写可维护、可扩展的应用程序至关重要。本章涵盖文件组织、命名空间、自动加载以及帮助您有效构建PHP项目的架构模式。

文件组织

基本文件结构

my-php-project/
├── public/              # Web可访问文件
│   ├── index.php       # 入口点
│   ├── css/
│   ├── js/
│   └── images/
├── src/                # 应用程序源代码
│   ├── Models/
│   ├── Controllers/
│   └── Views/
├── config/             # 配置文件
├── tests/              # 测试文件
├── vendor/             # Composer依赖
├── composer.json       # 依赖管理
└── .htaccess          # Apache配置

单文件程序

php
<?php
// simple_calculator.php

// 配置
error_reporting(E_ALL);
ini_set('display_errors', 1);

// 函数
function add($a, $b) {
    return $a + $b;
}

function subtract($a, $b) {
    return $a - $b;
}

function multiply($a, $b) {
    return $a * $b;
}

function divide($a, $b) {
    if ($b == 0) {
        throw new InvalidArgumentException("除数为零");
    }
    return $a / $b;
}

// 主程序逻辑
try {
    $num1 = 10;
    $num2 = 5;
    
    echo "加法: " . add($num1, $num2) . "\n";
    echo "减法: " . subtract($num1, $num2) . "\n";
    echo "乘法: " . multiply($num1, $num2) . "\n";
    echo "除法: " . divide($num1, $num2) . "\n";
} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

多文件程序

config/database.php

php
<?php
return [
    'host' => 'localhost',
    'database' => 'myapp',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8mb4'
];
?>

src/Database.php

php
<?php
class Database {
    private $connection;
    
    public function __construct($config) {
        $dsn = "mysql:host={$config['host']};dbname={$config['database']};charset={$config['charset']}";
        $this->connection = new PDO($dsn, $config['username'], $config['password']);
        $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    
    public function getConnection() {
        return $this->connection;
    }
}
?>

src/User.php

php
<?php
class User {
    private $db;
    
    public function __construct(Database $database) {
        $this->db = $database->getConnection();
    }
    
    public function create($name, $email) {
        $stmt = $this->db->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
        return $stmt->execute([$name, $email]);
    }
    
    public function findById($id) {
        $stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
        $stmt->execute([$id]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
}
?>

public/index.php

php
<?php
// 包含所需文件
require_once '../src/Database.php';
require_once '../src/User.php';

// 加载配置
$config = require '../config/database.php';

// 初始化应用程序
try {
    $database = new Database($config);
    $userModel = new User($database);
    
    // 应用程序逻辑
    $user = $userModel->findById(1);
    if ($user) {
        echo "欢迎," . htmlspecialchars($user['name']);
    } else {
        echo "用户未找到";
    }
} catch (Exception $e) {
    echo "错误: " . $e->getMessage();
}
?>

Include和Require

Include vs Require

php
<?php
// include - 如果找不到文件则继续执行(警告)
include 'optional_file.php';

// require - 如果找不到文件则停止执行(致命错误)
require 'essential_file.php';

// include_once - 只包含文件一次
include_once 'header.php';

// require_once - 只要求文件一次(推荐用于类)
require_once 'User.php';
?>

Include的最佳实践

php
<?php
// 使用绝对路径避免问题
require_once __DIR__ . '/config/database.php';

// 定义常用路径的常量
define('ROOT_PATH', __DIR__);
define('SRC_PATH', ROOT_PATH . '/src');
define('CONFIG_PATH', ROOT_PATH . '/config');

require_once SRC_PATH . '/User.php';
require_once CONFIG_PATH . '/database.php';
?>

命名空间

基本命名空间用法

php
<?php
// src/Models/User.php
namespace App\Models;

class User {
    public function getName() {
        return "Models命名空间中的用户";
    }
}
?>
php
<?php
// src/Controllers/User.php
namespace App\Controllers;

class User {
    public function index() {
        return "用户控制器";
    }
}
?>
php
<?php
// 使用命名空间类
require_once 'src/Models/User.php';
require_once 'src/Controllers/User.php';

// 完全限定名
$userModel = new \App\Models\User();
$userController = new \App\Controllers\User();

// 使用use语句
use App\Models\User as UserModel;
use App\Controllers\User as UserController;

$model = new UserModel();
$controller = new UserController();
?>

命名空间别名

php
<?php
namespace App\Services;

// 使用别名导入类
use App\Models\User as UserModel;
use App\Repositories\User as UserRepository;
use DateTime;

class UserService {
    private $userModel;
    private $userRepository;
    
    public function __construct() {
        $this->userModel = new UserModel();
        $this->userRepository = new UserRepository();
    }
    
    public function createUser($data) {
        $data['created_at'] = new DateTime();
        return $this->userRepository->save($data);
    }
}
?>

全局命名空间

php
<?php
namespace App\Models;

// 使用前导反斜杠访问全局类
$date = new \DateTime();
$pdo = new \PDO($dsn, $user, $pass);

// 或者导入它们
use DateTime;
use PDO;

$date = new DateTime();
$pdo = new PDO($dsn, $user, $pass);
?>

自动加载

手动自动加载

php
<?php
// autoload.php
spl_autoload_register(function ($className) {
    // 将命名空间转换为文件路径
    $file = str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
    $fullPath = __DIR__ . '/src/' . $file;
    
    if (file_exists($fullPath)) {
        require_once $fullPath;
    }
});

// 用法
require_once 'autoload.php';

// 这些类将被自动加载
$user = new App\Models\User();
$controller = new App\Controllers\UserController();
?>

PSR-4自动加载

php
<?php
// composer.json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
?>
php
<?php
// 运行后:composer dump-autoload
require_once 'vendor/autoload.php';

// 类会根据命名空间自动加载
$user = new App\Models\User();           // 加载 src/Models/User.php
$service = new App\Services\UserService(); // 加载 src/Services/UserService.php
?>

面向对象结构

类组织

php
<?php
namespace App\Models;

use DateTime;
use InvalidArgumentException;

/**
 * 用户模型类
 */
class User {
    // 常量
    const STATUS_ACTIVE = 1;
    const STATUS_INACTIVE = 0;
    
    // 属性
    private $id;
    private $name;
    private $email;
    private $status;
    private $createdAt;
    
    // 构造函数
    public function __construct($name, $email) {
        $this->setName($name);
        $this->setEmail($email);
        $this->status = self::STATUS_ACTIVE;
        $this->createdAt = new DateTime();
    }
    
    // Getters
    public function getId() {
        return $this->id;
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getEmail() {
        return $this->email;
    }
    
    // Setters with validation
    public function setName($name) {
        if (empty($name)) {
            throw new InvalidArgumentException("Name cannot be empty");
        }
        $this->name = trim($name);
    }
    
    public function setEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Invalid email format");
        }
        $this->email = $email;
    }
    
    // Business logic methods
    public function activate() {
        $this->status = self::STATUS_ACTIVE;
    }
    
    public function deactivate() {
        $this->status = self::STATUS_INACTIVE;
    }
    
    public function isActive() {
        return $this->status === self::STATUS_ACTIVE;
    }
    
    // Utility methods
    public function toArray() {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'status' => $this->status,
            'created_at' => $this->createdAt->format('Y-m-d H:i:s')
        ];
    }
}
?>

Interface and Abstract Classes

php
<?php
namespace App\Contracts;

interface UserRepositoryInterface {
    public function find($id);
    public function save(User $user);
    public function delete($id);
}
?>
php
<?php
namespace App\Repositories;

use App\Contracts\UserRepositoryInterface;
use App\Models\User;

abstract class BaseRepository {
    protected $connection;
    
    public function __construct($connection) {
        $this->connection = $connection;
    }
    
    abstract protected function getTableName();
}

class UserRepository extends BaseRepository implements UserRepositoryInterface {
    protected function getTableName() {
        return 'users';
    }
    
    public function find($id) {
        $stmt = $this->connection->prepare("SELECT * FROM {$this->getTableName()} WHERE id = ?");
        $stmt->execute([$id]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    
    public function save(User $user) {
        // Implementation
    }
    
    public function delete($id) {
        // Implementation
    }
}
?>

MVC Architecture Pattern

Model-View-Controller Structure

src/
├── Models/
│   ├── User.php
│   └── Product.php
├── Views/
│   ├── users/
│   │   ├── index.php
│   │   └── show.php
│   └── layouts/
│       └── main.php
├── Controllers/
│   ├── BaseController.php
│   └── UserController.php
└── Core/
    ├── Router.php
    └── Application.php

Base Controller

php
<?php
namespace App\Controllers;

abstract class BaseController {
    protected function render($view, $data = []) {
        extract($data);
        
        ob_start();
        include __DIR__ . "/../Views/{$view}.php";
        $content = ob_get_clean();
        
        include __DIR__ . "/../Views/layouts/main.php";
    }
    
    protected function redirect($url) {
        header("Location: {$url}");
        exit;
    }
    
    protected function json($data) {
        header('Content-Type: application/json');
        echo json_encode($data);
        exit;
    }
}
?>

User Controller

php
<?php
namespace App\Controllers;

use App\Models\User;
use App\Repositories\UserRepository;

class UserController extends BaseController {
    private $userRepository;
    
    public function __construct(UserRepository $userRepository) {
        $this->userRepository = $userRepository;
    }
    
    public function index() {
        $users = $this->userRepository->findAll();
        $this->render('users/index', ['users' => $users]);
    }
    
    public function show($id) {
        $user = $this->userRepository->find($id);
        
        if (!$user) {
            $this->redirect('/users');
        }
        
        $this->render('users/show', ['user' => $user]);
    }
    
    public function create() {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            try {
                $user = new User($_POST['name'], $_POST['email']);
                $this->userRepository->save($user);
                $this->redirect('/users');
            } catch (Exception $e) {
                $this->render('users/create', ['error' => $e->getMessage()]);
            }
        } else {
            $this->render('users/create');
        }
    }
}
?>

Configuration Management

Environment-Based Configuration

php
<?php
// config/app.php
return [
    'name' => $_ENV['APP_NAME'] ?? 'My Application',
    'debug' => $_ENV['APP_DEBUG'] ?? false,
    'url' => $_ENV['APP_URL'] ?? 'http://localhost',
    
    'database' => [
        'host' => $_ENV['DB_HOST'] ?? 'localhost',
        'name' => $_ENV['DB_NAME'] ?? 'myapp',
        'user' => $_ENV['DB_USER'] ?? 'root',
        'pass' => $_ENV['DB_PASS'] ?? '',
    ],
    
    'mail' => [
        'driver' => $_ENV['MAIL_DRIVER'] ?? 'smtp',
        'host' => $_ENV['MAIL_HOST'] ?? 'localhost',
        'port' => $_ENV['MAIL_PORT'] ?? 587,
    ]
];
?>

Configuration Class

php
<?php
namespace App\Core;

class Config {
    private static $config = [];
    
    public static function load($file) {
        $path = __DIR__ . "/../../config/{$file}.php";
        if (file_exists($path)) {
            self::$config[$file] = require $path;
        }
    }
    
    public static function get($key, $default = null) {
        $keys = explode('.', $key);
        $value = self::$config;
        
        foreach ($keys as $k) {
            if (!isset($value[$k])) {
                return $default;
            }
            $value = $value[$k];
        }
        
        return $value;
    }
}

// Usage
Config::load('app');
Config::load('database');

$appName = Config::get('app.name');
$dbHost = Config::get('app.database.host');
?>

Error Handling Structure

Custom Exception Classes

php
<?php
namespace App\Exceptions;

class ValidationException extends \Exception {
    private $errors;
    
    public function __construct($errors, $message = "Validation failed") {
        parent::__construct($message);
        $this->errors = $errors;
    }
    
    public function getErrors() {
        return $this->errors;
    }
}

class DatabaseException extends \Exception {
    // Custom database exception handling
}
?>

Global Error Handler

php
<?php
namespace App\Core;

use App\Exceptions\ValidationException;

class ErrorHandler {
    public static function register() {
        set_error_handler([self::class, 'handleError']);
        set_exception_handler([self::class, 'handleException']);
        register_shutdown_function([self::class, 'handleShutdown']);
    }
    
    public static function handleError($severity, $message, $file, $line) {
        if (!(error_reporting() & $severity)) {
            return false;
        }
        
        throw new \ErrorException($message, 0, $severity, $file, $line);
    }
    
    public static function handleException($exception) {
        if ($exception instanceof ValidationException) {
            self::renderValidationError($exception);
        } else {
            self::renderGenericError($exception);
        }
    }
    
    public static function handleShutdown() {
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR])) {
            self::handleException(new \ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
        }
    }
    
    private static function renderValidationError(ValidationException $e) {
        // Render validation error page
    }
    
    private static function renderGenericError(\Exception $e) {
        // Render generic error page
    }
}
?>

Application Bootstrap

Bootstrap File

php
<?php
// bootstrap.php

// Define constants
define('ROOT_PATH', __DIR__);
define('APP_PATH', ROOT_PATH . '/src');
define('CONFIG_PATH', ROOT_PATH . '/config');

// Load environment variables
if (file_exists(ROOT_PATH . '/.env')) {
    $lines = file(ROOT_PATH . '/.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        if (strpos($line, '=') !== false && strpos($line, '#') !== 0) {
            list($key, $value) = explode('=', $line, 2);
            $_ENV[trim($key)] = trim($value);
        }
    }
}

// Autoloader
require_once ROOT_PATH . '/vendor/autoload.php';

// Error handling
use App\Core\ErrorHandler;
ErrorHandler::register();

// Load configuration
use App\Core\Config;
Config::load('app');
Config::load('database');

// Set error reporting based on environment
if (Config::get('app.debug')) {
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
} else {
    error_reporting(0);
    ini_set('display_errors', 0);
}
?>

下一步

现在您已经了解了如何构建PHP程序,让我们在数据类型中详细探讨PHP的数据类型。

实践练习

  1. 创建一个具有适当命名空间组织的多文件项目
  2. 为博客应用程序实现简单的MVC结构
  3. 为您的自定义类设置自动加载
  4. 创建一个支持不同环境的配置系统
  5. 构建一个带有自定义异常的基本错误处理系统

理解程序结构对于构建可维护的PHP应用程序至关重要!

本站内容仅供学习和研究使用。