ReflectionClass类(反射)的使用

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);

/**
 * Human类
 */
class Human
{
    private string $name;
    private string $gender;
    private int $age;

    public function __construct(string $name, string $gender, int $age)
    {
        $this->name = $name;
        $this->gender = $gender;
        $this->age = $age;
    }

    public function profile(string $job): array
    {
        echo "俺叫{$this->name}{$this->gender}),今年{$this->age}岁,俺是一名{$job}。" . PHP_EOL;

        return [
            'name' => $this->name,
            'gender' => $this->gender,
            'age' => $this->age,
            'job' => $job,
        ];
    }
}

// 反射Animal类(类不存在)
try {
    $reflectionClass = new ReflectionClass('Animal');
} catch (ReflectionException $e) {
    echo 'ReflectionException: ' . $e->getMessage() . PHP_EOL; // ReflectionException: Class "Animal" does not exist
}

// 反射Human类(类存在)
try {
    $reflectionClass = new ReflectionClass('Human');
} catch (ReflectionException) {
    $reflectionClass = null;
}

// 如果反射Human类成功则进一步执行其它操作,如创建实例、调用方法、修改属性等操作
if ($reflectionClass instanceof ReflectionClass) {

    // 使用ReflectionClass::newInstanceArgs()创建实例,其中$args为构造方法的参数
    $args = ['张三', '男', 18];
    $instance = $reflectionClass->newInstanceArgs($args);

    // 获取实例的类名
    echo '$instance::class is ' . $instance::class . PHP_EOL; // $instance::class is Human

    // 利用反射判断profile()方法是否存在
    if ($reflectionClass->hasMethod('profile')) {
        // 利用反射获取profile()方法,并判断该方法是否为public,如果是public则调用
        // 重要说明:通过反射调用方法其实不受private和protected的限制,但是通过实例调用方法会受到限制,所以需要判断一下。
        $reflectionMethod = $reflectionClass->getMethod('profile');
        if ($reflectionMethod->isPublic()) {

            // 调用profile()方法  方式①:通过反射调用
            $profile1 = $reflectionMethod->invokeArgs($instance, ['程序猿']);
            echo print_r($profile1, true);
            // 俺叫张三(男),今年18岁,俺是一名程序猿。
            // Array
            // (
            //     [name] => 张三
            //     [gender] => 男
            //     [age] => 18
            //     [job] => 程序猿
            // )

            // 调用profile()方法  方式②:通过实例调用
            $profile2 = $instance->profile('攻城狮');
            echo print_r($profile2, true);
            // 俺叫张三(男),今年18岁,俺是一名攻城狮。
            // Array
            // (
            //     [name] => 张三
            //     [gender] => 男
            //     [age] => 18
            //     [job] => 攻城狮
            // )

        }
    }

    // 利用反射判断name、gender、age属性是否存在,若存在则修改
    $reflectionClass->hasProperty('name') && $reflectionClass->getProperty('name')->setValue($instance, '李四');
    $reflectionClass->hasProperty('gender') && $reflectionClass->getProperty('gender')->setValue($instance, '女');
    $reflectionClass->hasProperty('age') && $reflectionClass->getProperty('age')->setValue($instance, 17);
    $profile3 = $reflectionClass->getMethod('profile')->invokeArgs($instance, ['IT民工']);
    echo print_r($profile3, true);
    // 俺叫李四(女),今年17岁,俺是一名IT民工。
    // Array
    // (
    //     [name] => 李四
    //     [gender] => 女
    //     [age] => 17
    //     [job] => IT民工
    // )
}


//========== 总结 ==========//
// 1、在以具体业务为导向的开发中,反射使用得并不多,一般都是写框架才会使用反射(如ThinkPHP这类框架)。
// 2、反射的运行效率是非常低的,如果能使用new创建实例,就不要使用反射创建实例。
// 3、反射是非常强大和灵活的工具,它可以绕过可见性限制(private和protected)直接修改属性值和调用方法,甚至还可以直接修改可见性,
//    但是强烈不建议通过反射修改属性值,因为这种做法违反了封装原则,并可能导致运行时错误。

Copyright © 2024 码农人生. All Rights Reserved