当我们说一个编程语言支持反射时,我们指的是该语言具备在运行时检查、获取和修改其自身结构的能力。在 PHP 中,这被称为反射机制。反射提供了一组类和函数,使得我们可以在程序运行时动态地获取有关类、对象、函数、方法等信息,而无需在编写代码时硬编码这些信息。

现在,让我用更简单的语言解释一下反射的概念:

1. 类是什么?

在编程中,我们通常定义类来表示对象的模板。类包含属性(即对象的特征)和方法(即对象的行为)。例如,一个 User 类可能有属性如姓名和年龄,以及方法如设置姓名和获取年龄。

2. 反射是什么?

反射就像是在程序运行时用一面镜子看着你的代码。而这面镜子允许你:

  • 查看类的属性和方法是什么。
  • 动态地创建对象,就像你在代码中使用 new 关键字一样。
  • 调用类的方法,甚至在你不知道方法名的情况下。

3. 为什么我们需要反射?

有时候,我们可能想要编写更灵活、通用的代码,而不是写死在编写时就已经确定的东西。使用反射,我们可以:

  • 在运行时了解类的结构,而不是在写代码时硬编码这些信息。
  • 动态地创建对象和调用方法,这对于编写通用的、可配置的代码非常有用。
  • 在调试和测试中更灵活地查看和操作代码。

4. 反射的应用场景:

  • 通用代码: 如果你想要编写能够处理不同类的通用代码,而不必事先知道类的结构,反射就派上用场了。
  • 框架和库: 许多 PHP 框架和库使用反射来实现依赖注入、路由、ORM(对象关系映射)等功能。
  • 调试和测试: 反射允许你在运行时检查代码的结构,这在调试和测试时非常有帮助。

总之,反射是一种在程序运行时检查和操作代码结构的能力,使得我们能够写出更加灵活、通用的代码。

PHP的反射机制提供了一套反射API,用来访问和使用类、方法、属性、参数和注释等,比如可以通过一个对象知道这个对象所属的类,这个类包含哪些方法,这些方法需要传入什么参数,每个参数是什么类型等等,不用创建类的实例也可以访问类的成员和方法,就算类成员定义为 private也可以在外部访问。
反射是指在PHP运行状态中,扩展分析PHP程序,导出或提出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API。

PHP反射API由若干类组成,可帮助我们用来访问程序的元数据或者同相关的注释交互。借助反射我们可以获取诸如类实现了那些方法,创建一个类的实例(不同于用new创建),调用一个方法(也不同于常规调用),传递参数,动态调用类的静态方法。 反射API是PHP内建的OOP技术扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些OOP扩展被称为反射。

官方文档提供了诸如 ReflectionClass、ReflectionMethod、ReflectionObject、ReflectionExtension 等反射类及相应的API,我们用的比较多的是 ReflectionClass类、ReflectionObject 和ReflectionMethod类,

ReflectionClass 通过类名获取类的信息;

ReflectionObject 通过类的对象获取类的信息;

ReflectionMethod 获取一个方法的有关信息。


其他的还有

ReflectionException类

ReflectionFunction类

ReflectionExtension类

ReflectionFunctionAbstract 类

ReflectionGenerator类

ReflectionParameter 类

ReflectionProperty类

ReflectionType类

首先我们创建一个user类

<?php

class User
{
    /**
     * 性别常量,1代表男,2代表女
     */
    const USER_GENDER_1 = 1;
    const USER_GENDER_2 = 2;

    /**
     * 创建静态私有的变量保存该类对象
     */
    private static $instance;
    /**
     * 用户全局唯一id
     *
     * @var string
     */
    protected $globalId;
    /**
     * 用户姓名
     *
     * @var string
     */
    protected $name;
    /**
     * 用户性别,1男;2女
     *
     * @var int
     */
    protected $gender;
    /**
     * 用户手机号
     *
     * @var string
     */
    protected $mobile;

    /**
     * @return string
     */
    public function getGlobalId(): string
    {
        return $this->globalId;
    }

    /**
     * @param string $globalId
     */
    public function setGlobalId(string $globalId): void
    {
        $this->globalId = $globalId;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name): void
    {
        $this->name = $name;
    }

    /**
     * @return int
     */
    public function getGender(): int
    {
        return $this->gender;
    }

    /**
     * @param int $gender
     */
    public function setGender(int $gender): void
    {
        $this->gender = $gender;
    }

    /**
     * @return string
     */
    public function getMobile(): string
    {
        return $this->mobile;
    }

    /**
     * @param string $mobile
     */
    public function setMobile(string $mobile): void
    {
        $this->mobile = $mobile;
    }

    /**
     * 获取用户的单例实现
     * @return User
     */
    public static function getInstance()
    {
        //判断实例有无创建,没有的话创建实例并返回,有的话直接返回
        if (!(self::$instance instanceof self)) {
            self::$instance = new self();
        }
        return self::$instance;
    }

}

使用 PHP 的反射机制(Reflection)来动态获取和操作一个类(User 类)

$className = "User";
$class = new ReflectionClass($className); // 建立User类的反射类
$instance  = $class->newInstanceArgs(); // 相当于实例化User类

var_dump($class);
var_dump($instance);

$properties = $class->getProperties();

var_dump($properties);

foreach ($properties as $property) {
    echo $property->getName() . "\n";
}

$private_properties = $class->getProperties(ReflectionProperty::IS_PRIVATE);
var_dump($private_properties);


foreach ($properties as $property) {
    if ($property->isProtected()) {
        $docblock = $property->getDocComment();
        echo $docblock."\n";
    }
}

$methods = $class->getMethods();
var_dump($methods);

$isExistMethod = $class->hasMethod("setMobile");
var_dump($isExistMethod);
$isExistMethod = $class->hasMethod("test");
var_dump($isExistMethod);

$method = $class->getMethod("setMobile");
var_dump($method);


//设置用户姓名
$method = $class->getmethod('setName');// 获取User类中的setName方法
$method->invokeArgs($instance, ["joshua317"]);

//获取用户姓名
$method = $class->getmethod('getName');// 获取User类中的getName方法
$userName = $method->invoke($instance);//执行getName 方法
var_dump($userName);


$method = new ReflectionMethod('User', 'setName');
if ($method->isPublic() && !$method->isStatic()) {
    echo "function is protected";
}

// 参数个数
echo $method->getNumberOfParameters();

// 参数对象数组
$params = $method->getParameters();
var_dump($params);

//执行方法
$method->invokeArgs($instance, ["joshua"]);
//获取用户姓名
$method = $class->getmethod('getName');// 获取User类中的getName方法
$userName = $method->invoke($instance);//执行getName 方法
var_dump($userName);
image-20231204224932474

当你在 PHP 中编写代码时,通常你需要在代码中直接引用类、方法和属性。但是,有时候你可能想要在运行时了解类的结构,例如,获取类的属性、方法、检查方法的可见性等。这就是 PHP 的反射机制派上用场的地方。

PHP 反射机制的基本概念:

PHP 的反射机制提供了一组类,这些类允许你在运行时获取和操作类的信息。这些类主要位于 Reflection 类族中。

下面是你在提供的代码中用到的一些重要的 Reflection 类:

  1. ReflectionClass: 该类用于获取和操作类的信息。
  2. ReflectionProperty: 该类用于获取和操作类的属性信息。
  3. ReflectionMethod: 该类用于获取和操作类的方法信息。

现在,让我们逐步解释你提供的代码:

  1. 创建 ReflectionClass 对象: $className = "User"; $class = new ReflectionClass($className); 这里首先定义了一个类名字符串,然后使用 ReflectionClass 类创建了该类的反射对象 $class。这个反射对象包含了有关该类的各种信息。
  2. 实例化类: $instance = $class->newInstanceArgs(); 这里使用反射对象 $class 实例化了一个类对象 $instance。这相当于在代码中直接使用 new User()
  3. 获取类的属性: $properties = $class->getProperties(); 使用反射对象的 getProperties 方法获取了类的所有属性。这里没有指定参数,所以返回的是所有属性。
  4. 遍历属性并打印属性名: foreach ($properties as $property) { echo $property->getName() . "\n"; } 这个循环遍历了所有属性,并使用 getName 方法打印了属性名。
  5. 获取私有属性: $private_properties = $class->getProperties(ReflectionProperty::IS_PRIVATE); 使用 getProperties 方法的参数 ReflectionProperty::IS_PRIVATE 获取了私有属性。
  6. 遍历属性并打印文档注释(仅限受保护属性): foreach ($properties as $property) { if ($property->isProtected()) { $docblock = $property->getDocComment(); echo $docblock."\n"; } } 这个循环遍历了所有属性,如果属性是受保护的,则使用 getDocComment 方法获取文档注释并打印。
  7. 获取类的所有方法: $methods = $class->getMethods(); 使用 getMethods 方法获取了类的所有方法。
  8. 检查方法是否存在: $isExistMethod = $class->hasMethod("setMobile"); 使用 hasMethod 方法检查是否存在名为 "setMobile" 的方法。
  9. 获取方法的反射对象: $method = $class->getMethod("setMobile"); 使用 getMethod 方法获取名为 "setMobile" 的方法的反射对象。
  10. 调用方法并传递参数: $method->invokeArgs($instance, ["joshua317"]); 使用 invokeArgs 方法调用方法 "setMobile" 并传递参数 "joshua317"。
  11. 获取方法的返回值: $method = $class->getMethod('getName'); $userName = $method->invoke($instance); 使用 invoke 方法调用方法 "getName" 并获取返回值。
  12. 其他方法调用和信息获取: // 检查方法是否是公共方法且不是静态方法 $method = new ReflectionMethod('User', 'setName'); if ($method->isPublic() && !$method->isStatic()) { echo "function is protected"; } // 获取方法的参数个数和参数对象数组 echo $method->getNumberOfParameters(); $params = $method->getParameters(); 这部分代码演示了如何检查方法的可见性,获取参数的个数和参数对象数组。

这些操作都是基于反射机制,允许你在运行时动态获取和操作类的结构,而不需要硬编码类的信息。这在编写通用代码、调试和其他需要在运行时了解类结构的场景中非常有用。

逐代码块解释

$class = new ReflectionClass('User'); // 建立User这个类的反射类
$instance  = $class->newInstanceArgs(); // 相当于实例化User类

var_dump($class);
var_dump($instance);


结果
object(ReflectionClass)#1 (1) {
  ["name"]=>
  string(4) "User"
}
object(User)#2 (4) {
  ["globalId":protected]=>
  NULL
  ["name":protected]=>
  NULL
  ["gender":protected]=>
  NULL
  ["mobile":protected]=>
  NULL
}

获取属性(Properties):

$properties = $class->getProperties();

var_dump($properties);

foreach ($properties as $property) {
    echo $property->getName() . "\n";
}
//结果如下:
array(5) {
  [0]=>
  object(ReflectionProperty)#3 (2) {
    ["name"]=>
    string(8) "instance"
    ["class"]=>
    string(4) "User"
  }
  [1]=>
  object(ReflectionProperty)#4 (2) {
    ["name"]=>
    string(8) "globalId"
    ["class"]=>
    string(4) "User"
  }
  [2]=>
  object(ReflectionProperty)#5 (2) {
    ["name"]=>
    string(4) "name"
    ["class"]=>
    string(4) "User"
  }
  [3]=>
  object(ReflectionProperty)#6 (2) {
    ["name"]=>
    string(6) "gender"
    ["class"]=>
    string(4) "User"
  }
  [4]=>
  object(ReflectionProperty)#7 (2) {
    ["name"]=>
    string(6) "mobile"
    ["class"]=>
    string(4) "User"
  }
}

instance
globalId
name
gender
mobile

如果想要获取受保护的类或者私有的类

默认情况下,ReflectionClass会获取到所有的属性,private 和 protected的也可以。如果只想获取到private属性,就要额外传个参数:

$private_properties = $class->getProperties(ReflectionProperty::IS_PRIVATE);
var_dump($private_properties);
//结果如下:
array(1) {
  [0]=>
  object(ReflectionProperty)#8 (2) {
    ["name"]=>
    string(8) "instance"
    ["class"]=>
    string(4) "User"
  }
}

可用参数列表:

ReflectionProperty::IS_STATIC

ReflectionProperty::IS_PUBLIC

ReflectionProperty::IS_PROTECTED

ReflectionProperty::IS_PRIVATE

获取注释

foreach ($properties as $property) {
    if ($property->isProtected()) {
        $docblock = $property->getDocComment();
        echo $docblock."\n";
    }
}
结果如下:
/**
     * 用户全局唯一id
     *
     * @var string
     */
/**
     * 用户姓名
     *
     * @var string
     */
/**
     * 用户性别,1男;2女
     *
     * @var int
     */
/**
     * 用户手机号
     *
     * @var string
     */

获取类的方法

getMethods() 来获取到类的所有methods。

hasMethod(string)  是否存在某个方法

getMethod(string)  获取方法
$methods = $class->getMethods();
var_dump($methods);

//结果如下:
array(9) {
  [0]=>
  object(ReflectionMethod)#9 (2) {
    ["name"]=>
    string(11) "getGlobalId"
    ["class"]=>
    string(4) "User"
  }
  [1]=>
  object(ReflectionMethod)#10 (2) {
    ["name"]=>
    string(11) "setGlobalId"
    ["class"]=>
    string(4) "User"
  }
  [2]=>
  object(ReflectionMethod)#11 (2) {
    ["name"]=>
    string(7) "getName"
    ["class"]=>
    string(4) "User"
  }
  [3]=>
  object(ReflectionMethod)#12 (2) {
    ["name"]=>
    string(7) "setName"
    ["class"]=>
    string(4) "User"
  }
  [4]=>
  object(ReflectionMethod)#13 (2) {
    ["name"]=>
    string(9) "getGender"
    ["class"]=>
    string(4) "User"
  }
  [5]=>
  object(ReflectionMethod)#14 (2) {
    ["name"]=>
    string(9) "setGender"
    ["class"]=>
    string(4) "User"
  }
  [6]=>
  object(ReflectionMethod)#15 (2) {
    ["name"]=>
    string(9) "getMobile"
    ["class"]=>
    string(4) "User"
  }
  [7]=>
  object(ReflectionMethod)#16 (2) {
    ["name"]=>
    string(9) "setMobile"
    ["class"]=>
    string(4) "User"
  }
  [8]=>
  object(ReflectionMethod)#17 (2) {
    ["name"]=>
    string(11) "getInstance"
    ["class"]=>
    string(4) "User"
  }
}
$isExistMethod = $class->hasMethod("setMobile");
var_dump($isExistMethod);
$isExistMethod = $class->hasMethod("test");
var_dump($isExistMethod);

//结果如下:
bool(true)
bool(false)
$method = $class->getMethod("setMobile");
var_dump($method);
//结果如下:
object(ReflectionMethod)#18 (2) {
  ["name"]=>
  string(9) "setMobile"
  ["class"]=>
  string(4) "User"
}

执行类的方法

//设置用户姓名
$method = $class->getmethod('setName');// 获取User类中的setName方法
$method->invokeArgs($instance, ["joshua317"]);

//获取用户姓名
$method = $class->getmethod('getName');// 获取User类中的getName方法
$userName = $method->invoke($instance);//执行getName 方法
var_dump($userName);

//结果如下:
joshua317

ReflectionMethod,获取User类的某个方法的信息

1.是否"public"、"protected"、"private" 、"static"类型 
2.方法的参数列表 
3.方法的参数个数 
4.反调用类的方法
// 获取方法修饰类型
$method = new ReflectionMethod('User', 'getName');
if ($method->isPublic() && !$method->isStatic()) {
    echo "function is public";
}

// 参数个数
echo $method->getNumberOfParameters();

// 参数对象数组
$params = $method->getParameters();
var_dump($params);

//执行方法
$method->invokeArgs($instance, ["joshua"]);
//获取用户姓名
$method = $class->getmethod('getName');// 获取User类中的getName方法
$userName = $method->invoke($instance);//执行getName 方法
var_dump($userName);

//结果如下:
function is public
1
array(1) {
  [0]=>
  object(ReflectionParameter)#18 (1) {
    ["name"]=>
    string(4) "name"
  }
joshua

Views: 2

邮箱:zzpqwetvg@gmail.com
最后更新于 2023-12-05