Vanson's Eternal Blog

PHP夯实基础

Php basic.png
Published on
/32 mins read/---

PHP

基础

OOP

对象与类的关系

  • 类(Class): 是创建对象的蓝图或模板,定义了对象的属性和方法
  • 对象(Object): 是类的实例,具有类定义的属性和方法
class Person {
    public $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function greet() {
        return "Hello, my name is {$this->name}";
    }
}
 
$person = new Person("Alice");
echo $person->greet(); // Hello, my name is Alice

封装

数据隐藏: 限制对对象内部状态的直接访问

接口暴露: 提供公共方法来操作对象

PHP通过访问修饰符实现封装:

  • public: 任何地方可访问
  • protected: 类内部和子类中可访问
  • private: 仅在类内部可访问
class BankAccount {
    private $balance = 0;
    
    public function deposit($amount) {
        if ($amount > 0) {
            $this->balance += $amount;
        }
    }
    
    public function getBalance() {
        return $this->balance;
    }
}
 
$account = new BankAccount();
$account->deposit(100);
echo $account->getBalance(); // 100
// echo $account->balance; // 报错: 无法访问私有属性

继承(Inheritance)

继承允许新类(子类)获取现有类(父类)的属性和方法,并可以扩展或修改它们。

继承类型:

  • 单继承(PHP只支持单继承)
  • 多级继承
class Animal {
    protected $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function eat() {
        return "{$this->name} is eating";
    }
}
 
class Dog extends Animal {
    public function bark() {
        return "{$this->name} says woof!";
    }
}
 
$dog = new Dog("Buddy");
echo $dog->eat();  // Buddy is eating
echo $dog->bark(); // Buddy says woof!

多态(Polymorphism)

多态允许不同类的对象对相同消息(方法调用)做出不同响应。

实现方式:

  • 方法重写(Override)
  • 接口实现
interface Shape {
    public function area();
}
 
class Circle implements Shape {
    private $radius;
    
    public function __construct($radius) {
        $this->radius = $radius;
    }
    
    public function area() {
        return pi() * pow($this->radius, 2);
    }
}
 
class Square implements Shape {
    private $side;
    
    public function __construct($side) {
        $this->side = $side;
    }
    
    public function area() {
        return pow($this->side, 2);
    }
}
 
function printArea(Shape $shape) {
    echo "Area: " . $shape->area() . "\n";
}
 
printArea(new Circle(5)); // Area: 78.539816339745
printArea(new Square(4)); // Area: 16

抽象类

抽象类是不能实例化的类,用于定义子类必须实现的接口。

abstract class Vehicle {
    abstract public function startEngine();
    
    public function stopEngine() {
        return "Engine stopped";
    }
}
 
class Car extends Vehicle {
    public function startEngine() {
        return "Car engine started";
    }
}
 
$car = new Car();
echo $car->startEngine(); // Car engine started
echo $car->stopEngine();  // Engine stopped

接口

接口定义了一组方法规范,类可以实现多个接口。

interface Logger {
    public function log($message);
}
 
interface Notifier {
    public function sendNotification($message);
}
 
class FileLogger implements Logger {
    public function log($message) {
        file_put_contents('log.txt', $message, FILE_APPEND);
    }
}
 
class EmailNotifier implements Logger, Notifier {
    public function log($message) {
        // 记录日志
    }
    
    public function sendNotification($message) {
        // 发送邮件
    }
}

魔术方法

  • __construct() 对象创建时
  • __destruct() 对象销毁时
  • __get() 访问不可访问属性时
  • __set() 设置不可访问属性时
  • __call() 调用不可访问方法时
  • __toString() 对象被当作字符串使用时
class Magic {
    private $data = [];
    
    public function __set($name, $value) {
        $this->data[$name] = $value;
    }
    
    public function __get($name) {
        return $this->data[$name] ?? null;
    }
    
    public function __toString() {
        return json_encode($this->data);
    }
}
 
$magic = new Magic();
$magic->property = 'value';
echo $magic; // {"property":"value"}

特质(Trait)

PHP使用特质(Trait)解决单继承限制,允许代码复用。

trait Loggable {
    public function log($message) {
        echo "[LOG] {$message}\n";
    }
}
 
trait Timestampable {
    public function timestamp() {
        return date('Y-m-d H:i:s');
    }
}
 
class Order {
    use Loggable, Timestampable;
    
    public function process() {
        $this->log("Order processed at " . $this->timestamp());
    }
}
 
$order = new Order();
$order->process();

类型声明

PHP7+增强了类型声明支持:

class Calculator {
    public function add(float $a, float $b): float {
        return $a + $b;
    }
    
    public function concat(string ...$strings): string {
        return implode(' ', $strings);
    }
}
 
$calc = new Calculator();
echo $calc->add(1.5, 2.5); // 4.0
echo $calc->concat("Hello", "world"); // Hello world

依赖注入(DI)

依赖注入是一种实现控制反转(IoC)的技术,减少类之间的直接依赖。

interface Database {
    public function query($sql);
}
 
class MySQLDatabase implements Database {
    public function query($sql) {
        echo "Executing MySQL query: {$sql}\n";
    }
}
 
class UserRepository {
    private $db;
    
    public function __construct(Database $db) {
        $this->db = $db;
    }
    
    public function getUsers() {
        return $this->db->query("SELECT * FROM users");
    }
}
 
$db = new MySQLDatabase();
$userRepo = new UserRepository($db);
$userRepo->getUsers();

匿名类

$logger = new class {
    public function log(string $msg): void
    {
        echo "[LOG] $msg\n";
    }
};
$logger->log('hello');
 

接口和抽象类的区别

对比项接口(Interface);抽象类(Abstract Class)
定义关键字interfaceabstract class
方法实现所有方法必须为抽象方法(无实现体)可包含抽象方法(无实现体)和具体方法(有实现体)
属性/成员变量仅能定义常量(public static final可定义普通属性、静态属性和常量,支持多种访问修饰符
继承/实现方式类通过implements实现多个接口类通过extends继承单个抽象类
构造方法不允许定义构造方法可定义构造方法(用于子类初始化)
方法访问修饰符所有方法默认为public支持publicprotectedprivate
多继承支持支持(一个类可实现多个接口)不支持(PHP为单继承)
设计目的强调行为契约(“能做什么”)
 
<?php
// 接口定义示例
interface AnimalInterface {
    const TYPE = '生物'; // 常量
    public function makeSound(); // 抽象方法
    public function eat(string $food); // 抽象方法
}
 
// 抽象类定义示例
abstract class AnimalAbstract {
    protected $name; // 成员属性
    public function __construct(string $name) { // 构造方法
        $this->name = $name;
    }
    abstract public function run(); // 抽象方法
    public function sleep() { // 具体方法
        echo "{$this->name}正在睡觉...\n";
    }
}
 
// 实现接口和继承抽象类
class Dog extends AnimalAbstract implements AnimalInterface {
    public function makeSound() {
        echo "汪汪!\n";
    }
    public function eat(string $food) {
        echo "吃{$food}\n";
    }
    public function run() {
        echo "{$this->name}快速奔跑\n";
    }
}
 
// 使用示例
$dog = new Dog('阿黄');
$dog->makeSound(); // 输出: 汪汪!
$dog->eat('骨头'); // 输出: 吃骨头
$dog->sleep();     // 输出: 阿黄正在睡觉...
$dog->run();       // 输出: 阿黄快速奔跑
?>
 

PHP 的生命周期/启动流程

完整的生命周期为

  1. 模块初始化
  2. 请求初始化
  3. 请求处理
  4. 请求关闭
  5. 模块关闭。
  • cli 模式下,每个脚本都会完整的执行上面的五大阶段;
  • 对于 fastcgi 模式而言,只在启动时会执行模块初始化,之后的请求都走了请求初始化、处理请求、请求关闭三大阶段,在 fastcgi 关闭时执行模块关闭阶段。各个扩展的加载也是在模块初始化阶段完成的。

PHP 8+新特性

  • 联合类型:允许变量、参数或返回值声明多种可能类型(如string|int)。
  • 命名参数:调用函数时可指定参数名,提升代码可读性。
  • 属性(Attributes):替代PHPDoc注释的元数据声明方式。
  • match表达式:更强大的switch替代方案,可直接返回值。
  • nullsafe运算符:?->在链式调用中遇到null时停止执行而不报错。
  • 构造函数属性提升:简化类属性声明和构造函数赋值。
  • JIT编译器:显著提升计算密集型任务性能。

Opcache设置问题

  • 缓存不更新:开发环境未关闭opcache.validate_timestamps,导致代码更改不生效。
  • 内存不足:opcache.memory_consumption设置过小,导致缓存频繁失效。
  • 脚本限制:opcache.max_accelerated_files不足,导致部分文件未被缓存。
  • 权限问题:Opcache缓存目录不可写。
  • 符号链接问题:使用符号链接时路径解析错误。
  • 预加载冲突:opcache.preload配置错误,导致类加载问题。
  • 与Xdebug冲突:同时启用可能导致性能下降或调试问题。

PHP 的(内存)垃圾回收机制

每一个变量对应一个 zval 数据结构,在该结构内还有一个 val 结构体,该结构体内有一个引用计数(php7 而言,对于 php5,这个引用计数是保存在 zval 结构中的),标识该对象的引用数,当对象的引用计数为 0 时代表这个对象可被回收。

对象的 refcount 减少的时机: 修改变量、函数返回(释放局部变量)、unset 变量

对于数组和对象而言,可能存在变量中的成员引用变量本身的情况,也就是循环引用,这样会造成这个变量永远不会被内存回收,而成为垃圾。

PHP 里对于这种情况给出了垃圾回收机制 如果数组、对象的引用计数减少而且不为零,则认为他们可能是垃圾,把他们放到垃圾收集器里。

等垃圾收集器到了一定的数量之后,进行垃圾处理:对所有可能的垃圾 refcount 减 1,如果为 1,说明是垃圾,则进行内存回收;如果不为 1,说明还有其他变量在使用,refcount 重新加 1;

这种对象复用以及垃圾回收机制在其他语言中也有体现:redis 中也使用了引用计数表示每个对象的引用数量。

如果我的网站用的utf-8编码,为防止乱码出现,都需要注意哪些地方?

从以下几个方面考虑:

  • 数据库中库和表都用utf8编码php连接mysql,指定数据库编码为utf8 mysql_query("set names utf8");
  • php文件指定头部编码为utf-8 header("content-type:text/html;charset=utf-8");
  • 网站下所有文件的编码为utf8,html文件指定编码为utf-8;

什么是单点入口呢?

所谓单点入口就是整个应用程序只有一个入口,所有的实现都通过这个入口来转发。比如Thinkphp使用index.php作为程序的单点入口,当然这个是可以由你自己任意控制的。

作用:

  • 定义框架路径、项目路径和项目名称(可选)
  • 定义调试模式和运行模式的相关常量(可选)
  • 载入框架入口文件(必须)

单点入口有几大好处:

  1. 一些系统全局处理的变量,类,方法都可以在这里进行处理。比如说你要对数据进行初步的过滤,你要模拟session处理,你要定义一些全局变量,甚至你要注册一些对象或者变量到注册器里面。
  2. 程序的架构更加清晰明了。

PHP 的控制反转

一种设计思想,用于降低代码之间的耦合度,增强代码的灵活性和可维护性。将组件间的依赖关系从程序内部提到外部来管理。在PHP中,控制反转通常通过依赖注入(Dependency Injection,DI)来实现。

  • 构造器注入
  • Setter方法注入
  • 接口注入(最建议)

合并数组

功能array_mergearray_merge_recursivearray_combine+
合并方式合并数组,覆盖重复键名递归合并数组,合并重复键名的值组合两个数组,第一个数组的值作为键名合并数组,保留第一个数组的重复键名
适用场景一般数组合并多维数组合并创建键值对数组保留第一个数组的键值
键名重复处理覆盖合并为数组无重复键名,否则报错保留第一个数组的值

常用的魔术方法有哪些?

这些方法在特定的情况下会被自动调用,从而提供一些强大的功能,例如重载、序列化、自动加载等。

  • __construct() 实例化类时自动调用。
  • __destruct() 类对象使用结束时自动调用。
  • __set() 在给未定义的属性赋值的时候调用。
  • __get() 调用未定义的属性时候调用。
  • __isset() 使用isset()或empty()函数时候会调用。
  • __unset() 使用unset()时候会调用。
  • __sleep() 使用serialize序列化时候调用。
  • __wakeup() 使用unserialize反序列化的时候调用。
  • __call() 调用一个不存在的方法的时候调用。
  • __callStatic()调用一个不存在的静态方法调用。
  • __toString() 把对象转换成字符串的时候会调用。比如 echo。
  • __invoke() 当尝试把对象当方法调用时调用。
  • __set_state() 当使用var_export()函数时候调用。接受一个数组参数。
  • __clone() 当使用clone复制一个对象时候调用。

$this和self、parent这三个关键词

分别代表什么?在哪些场合下使用?

  • $this 当前对象:$this在当前类中使用,使用->调用属性和方法。
  • self 当前类:self也在当前类中使用,不过需要使用::调用。
  • parent 当前类的父类:parent在类中使用。

常量

类中如何定义常量、如何类中调用常量、如何在类外调用常量。

类中的常量也就是成员常量,常量就是不会改变的量,是一个恒值。 定义常量使用关键字const.例如:const PI = 3.1415326; 无论是类内还是类外,常量的访问和变量是不一样的,常量不需要实例化对象,访问常量的格式都是类名加作用域操作符号(双冒号)来调用。即:类名 :: 类常量名;

__autoload()方法的工作原理是什么?

使用这个魔术函数的基本条件是类文件的文件名要和类的名字保持一致。

  1. 当程序执行到实例化某个类的时候,如果在实例化前没有引入这个类文件,那么就自动执行__autoload()函数。
  2. 这个函数会根据实例化的类的名称来查找这个类文件的路径,当判断这个类文件路径下确实存在这个类文件后就执行include或者require来载入该类,然后程序继续执行,如果这个路径下不存在该文件时就提示错误。

使用自动载入的魔术函数可以不必要写很多个include或者require函数。

trait 和 interface

trait 特质是一种代码复用机制,允许在类中插入一段预定义的代码块。特质可以包含方法和属性。

trait Greeting {
    public function sayHello() {
        echo "Hello!\n";
    }
}
 
class User {
    use Greeting;
}
 
$user = new User();
$user->sayHello();  // 输出:Hello!

interface 接口中的方法不能有具体实现,只能定义方法的签名(方法名、参数和返回值类型)。

interface Animal {
    public function makeSound(): string;
}
 
class Dog implements Animal {
    public function makeSound(): string {
        return "Bark";
    }
}
 
class Cat implements Animal {
    public function makeSound(): string {
        return "Meow";
    }
}

PHP超全局变量的列表

  • $_GET:用于获取通过URL参数传递的GET请求数据。
  • $_POST:用于获取通过表单提交的POST请求数据。
  • $_REQUEST:包含通过GET、POST或COOKIE传递的请求数据。
  • $_COOKIE:用于获取客户端发送的Cookie数据。
  • $_SESSION:用于存储会话数据,这些数据在用户访问期间会持续存在。
  • $_SERVER:包含服务器环境信息,例如请求方法、脚本路径、客户端IP等。
  • $_ENV:用于访问环境变量。
  • $_FILES:用于处理文件上传。
  • $GLOBALS:包含当前脚本中所有全局变量的数组。

PHP如何实现页面跳转

使用header()函数 通过header()函数发送HTTP头信息,实现页面跳转。

header("Location: https://www.example.com");
exit;  // 跳转后停止脚本执行

使用HTML的<meta>标签 在HTML中使用<meta>标签实现页面跳转。

echo '<meta http-equiv="refresh" content="0;url=https://www.example.com">';

防止脚本攻击函数

htmlspecialchars 主要用于处理用户输入或动态生成的HTML内容,防止XSS攻击。

htmlentities 适用于需要将整个字符串完全转换为HTML实体的场景,例如显示用户输入的HTML代码。

is_writeable()函数存在Bug

无法准确判断一个目录/文件是否可写,请写一个函数来判断目录/文件是否绝对可写

在windows中,当文件只有只读属性时,is_writeable()函数才返回false,当返回true时,该文件不一定是可写的。 如果是目录,在目录中新建文件并通过打开文件来判断; 如果是文件,可以通过打开文件(fopen),来测试文件是否可写。

在Unix中,当php配置文件中开启safe_mode时(safe_mode=on),is_writeable()同样不可用。 读取配置文件是否safe_mode是否开启。

创建Composer包

  • 初始化项目:mkdir my-package && cd my-package,然后运行composer init。
  • 配置composer.json:指定name、description、type、license、autoload等信息。
  • 设置命名空间和自动加载:如"psr-4": {"MyVendor\\MyPackage\\": "src/"}
  • 编写包代码:在src/目录下创建PHP类,遵循PSR-4标准。
  • 添加测试:使用PHPUnit或其他测试框架编写测试用例。
  • 发布到Packagist:将代码推送到GitHub/GitLab等平台,在Packagist.org提交仓库URL,设置GitHub服务钩子自动更新。
  • 版本控制:使用语义化版本控制(SemVer),通过git tag标记版本。
  • 文档编写:添加README.md,说明包用途、安装方法和使用示例。

ThinkPHP

核心架构

核心层

核心层是 ThinkPHP 的基础部分,提供了框架运行所需的基本功能和工具。

  • 请求处理:接收和解析用户请求,提取请求参数。
  • 路由分发:根据请求的 URL 和配置的路由规则,将请求分发到对应的控制器和方法。
  • 模板引擎:支持多种模板引擎,用于视图层的渲染。
  • 自动加载:支持 PSR-4 和 PSR-0 标准的自动加载机制,方便类的加载。
  • 配置管理:提供灵活的配置文件管理,支持多种配置方式。
  • 日志记录:记录运行日志,便于调试和问题排查。

行为层

行为层提供了一些常用的行为(Behavior),这些行为可以在框架运行的各个阶段被触发和执行。行为层的主要功能包括:

  • 日志记录:记录系统运行日志。
  • 性能监控:监控系统性能,记录请求的执行时间等信息。
  • 安全防护:提供安全防护机制,如防止 SQL 注入、XSS 攻击等。
  • 图片处理:提供图片上传、裁剪、缩放等操作。
  • 验证码生成:生成验证码,用于用户验证。

服务层

服务层是业务逻辑的具体实现部分,通常由开发者根据具体业务需求开发。

  • 模型层(Model):封装数据库操作,提供数据的增删改查等操作。
  • 控制器层(Controller):处理用户请求,调用模型层获取数据,并将数据传递给视图层。
  • 视图层(View):负责将数据渲染成用户界面,通常使用模板引擎来实现。
  • 服务类(Service):封装复杂的业务逻辑,提供复用的业务方法。

运行机制

  • 入口(Entry):接收用户请求的部分,通常是 index.php 文件。入口文件负责初始化框架环境,加载配置文件,启动框架。
  • 调度(Dispatch):决定如何处理请求的部分。调度器根据配置文件解析 URL,找到对应的控制器和动作,并将其交给控制器处理。
  • 响应(Response):向客户端发送结果的部分。控制器处理完请求后,将结果传递给响应器,响应器负责将结果发送给客户端。

核心组件

  • 路由组件

    • 功能:解析 URL,根据路由规则将请求分发到对应的控制器和方法。
    • 实现:通过 Route 类实现,支持多种路由规则,如正则表达式、动态路由等。
  • 模型组件

    • 功能:封装数据库操作,提供数据的增删改查等操作。
    • 实现:通过 Model 类实现,支持多种数据库驱动,如 MySQL、SQLite 等。
  • 视图组件

    • 功能:负责将数据渲染成用户界面。
    • 实现:支持多种模板引擎,如 Smarty、Twig 等,默认使用内置的模板引擎。
  • 日志组件

    • 功能:记录系统运行日志,便于调试和问题排查。
    • 实现:通过 Log 类实现,支持多种日志存储方式,如文件、数据库等。
  • 配置组件

    • 功能:提供灵活的配置文件管理,支持多种配置方式。
    • 实现:通过 Config 类实现,支持从文件、环境变量等加载配置。

扩展机制

ThinkPHP 提供了丰富的扩展机制,允许开发者根据需要扩展框架的功能:

  • 行为扩展:通过定义行为类,可以在框架运行的各个阶段插入自定义逻辑。
  • 插件扩展:通过定义插件类,可以在控制器中调用插件提供的功能。
  • 服务提供者:通过定义服务提供者类,可以注册和管理应用服务。
  • 中间件:通过定义中间件类,可以在请求处理过程中插入自定义逻辑。

自定义路由解析和调度器

  • 实现自定义路由解析器:首先,需要继承think\Route类并实现自己的路由解析器。在这个解析器中,可以实现自己的路由规则和匹配逻辑。
  • 注册自定义路由解析器:然后,在route.php配置文件中注册自己的路由解析器。例如,可以通过register方法注册自己的路由解析器。
  • 实现自定义调度器:接下来,可以继承think\App类并实现自己的调度器。在这个调度器中,可以实现自己的调度规则和分发逻辑。
  • 调用自定义调度器:最后,在入口文件index.php中调用自己的调度器。例如,可以通过app()->run方法启动自己的调度器。

ThinkPHP应用的性能

  • 数据库优化:在开发过程中,需要注意数据库的设计和优化,包括合理设计表结构、优化查询语句、使用索引等。此外,也可以利用缓存技术来降低数据库的访问压力,提高查询效率。
  • 响应时间优化:可以利用ThinkPHP提供的缓存技术,减少不必要的计算和数据库访问。此外,还可以采用异步任务和队列等方式来分散请求的压力,降低服务器响应时间。
  • 内存消耗优化:可以采用分页技术,限制一次查询返回的数据量,降低内存占用;也可以利用静态代理,将频繁访问的数据存储在内存中,减少对数据库的直接访问。
  • 代码优化:保持代码简洁和高效,避免冗余代码和无效循环等。
  • 合理利用框架提供的特性:利用ThinkPHP提供的特性,如路由调度、缓存、自动加载等,可以提高程序的整体性能。
  • 及时发布新版本:及时更新ThinkPHP的新版本,可以修复已知问题,优化性能。

Laravel

Container 容器

Laravel 容器是一个强大的依赖注入容器,用于管理类的依赖关系和实例化过程。

  • 依赖注入:自动解析类的依赖关系。
  • 单例管理:支持单例模式,确保某些类的实例在整个应用中只有一个。
  • 接口绑定:将接口绑定到具体的实现类。
  • 闭包绑定:支持通过闭包动态生成对象。

容器的源码位于 vendor/laravel/framework/src/Illuminate/Container/Container.php

IOC 控制反转

控制反转(Inversion of Control, IoC)是一种设计原则,用于将类的依赖关系从类的内部反转到类的外部。

通过控制反转,类的依赖关系由外部注入,而不是在类的内部直接实例化。这有助于降低代码的耦合度,提高代码的可维护性和可测试性。

class UserController {
    protected $userService;
 
    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }
}

UserController 的依赖 UserService 是通过构造函数注入的,而不是在 UserController 内部直接实例化。

DI 依赖注入

依赖注入(Dependency Injection, DI)是控制反转的一种实现方式,通过函数参数或构造函数参数将依赖关系注入到类中。

依赖注入有助于提高代码的模块化、可测试性和灵活性。

构造函数注入

class UserService {
    private $userRepository;
    
    public function __construct(UserRepository $userRepository) {
        $this->userRepository = $userRepository;
    }
    
    public function getUser($id) {
        return $this->userRepository->find($id);
    }
}

方法注入

class ReportGenerator {
    public function generate(ExportFormatter $formatter, $data) {
        return $formatter->format($data);
    }
}
 
// 使用示例
$generator = new ReportGenerator();
$generator->generate(app(ExcelFormatter::class), $data);

接口/契约注入

interface LoggerInterface {
    public function log($message);
}
 
class FileLogger implements LoggerInterface {
    public function log($message) {
        file_put_contents('app.log', $message, FILE_APPEND);
    }
}
 
// 绑定接口到实现
$this->app->bind(LoggerInterface::class, FileLogger::class);
 
// 使用
class OrderProcessor {
    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger;
    }
}

Contract 契约

契约(Contract)是 Laravel 中的一组接口,用于定义类的行为和方法。

契约提供了一种标准化的方式来定义类的功能,使得不同的实现类可以互换使用。

契约通常位于 vendor/laravel/framework/src/Illuminate/Contracts 命名空间下。

namespace Illuminate\Contracts\Auth;
 
interface Guard {
    public function user();
    public function check();
    public function guest();
}
 

为什么使用契约?

  • 低耦合:依赖接口而非具体实现
  • 可替换性:轻松切换实现(如从文件缓存切换到Redis)
  • 可测试性:更容易模拟依赖

Provider 服务提供者

服务提供者(Service Provider)是 Laravel 中用于注册服务和绑定依赖关系的类。

服务提供者通常位于 app/Providers 目录下,它们通过 register 和 boot 方法来注册服务和执行初始化操作。

namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider {
    public function register() {
        // 注册服务
        $this->app->bind('App\Contracts\UserRepository', 'App\Repositories\DbUserRepository');
    }
 
    public function boot() {
        // 初始化操作
    }
}
 
 
 

依赖解析过程

当Laravel解析一个类时,其过程如下:

  • 检查是否已注册显式绑定
  • 如果没有,使用反射自动解析
  • 递归解析所有依赖
  • 返回完全构建的对象
← Previous postJavaScript夯实基础
Next post →MySQL夯实基础