PHP反射机制总结及在设计模式中的应用

本文共有8245个字,页面加载耗时0.001秒,关键词:php反射机制

PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。

其功能十分强大,经常用于高扩展的PHP框架,自动加载插件,自动生成文档,甚至可以用来扩展PHP语言。由于它是PHP內建的oop扩展,为语言本身自带的特性,所以不需要额外添加扩展或者配置就可以使用。更多内容见官方文档。

1.具体类目

1.1.ReflectionClass

主要是读取被反射类的信息,用于文档生成或检查是否包含某种信息 |
  • ReflectionClass::getDocComment — 获取文档注释
  • ReflectionClass::getEndLine — 获取最后一行的行数
  • ReflectionClass::getInterfaces — 获取接口
  • ReflectionClass::getMethods — 获取方法的数组

测试代码:

<?php

/**
 * Class Demo1
 */
class Demo1 {
    const NAME = 'tom';
    const AGE = 18;

    /**
     * 测试用的方法
     * @param $arg String 随便一个参数
     */
    public function test($arg) {
        echo "hello I am Demo1.test()," . $arg;
    }
}

$demo1 = new ReflectionClass('Demo1');

print_r($demo1->getConstants());                // Array(NAME] => tom,[AGE] => 18)

print_r($demo1->getConstant('NAME'));     // tom

print_r($demo1->getConstant('GENDER'));   // 空白

print_r($demo1->getDocComment());
///**
// * Class Demo1
// */

$reflectionMethod = $demo1->getMethod('test');
echo $reflectionMethod->getDocComment();
///**
// * 测试用的方法
// * @param $arg String 随便一个参数
// */

print_r('test args number is : ' . $reflectionMethod->getNumberOfParameters());  // test args number is : 1

1.2 ReflectionExtension

报告了PHP扩展的有关信息,只能反射拓展的内容
  • ReflectionExtension::getClasses — Gets classes
  • ReflectionExtension::getClassNames — 获取类名称
  • ReflectionExtension::getDependencies — 获取依赖
  • ReflectionExtension::getFunctions — 获取扩展中的函数
  • ReflectionExtension::getINIEntries — 获取ini配置
  • ReflectionExtension::getName — 获取扩展名称
  • ReflectionExtension::getVersion — 获取扩展版本号
  • ReflectionExtension::info — 输出扩展信息
  • ReflectionExtension::isPersistent — 返回扩展是否持久化载入
  • ReflectionExtension::isTemporary — 返回扩展是否是临时载入
$reflectionExtension = new ReflectionExtension('zlib');
//var_dump($reflectionExtension->getFunctions());
var_dump($reflectionExtension->getName());  // string(4) "zlib"
//var_dump($reflectionExtension->info());

1.3 ReflectionMethod ☆☆☆☆☆

ReflectionMethod 类报告了一个方法的有关信息。
  • ReflectionMethod::getModifiers — 获取方法的修饰符
  • ReflectionMethod::invoke — Invoke(可变参数)
  • ReflectionMethod::invokeArgs — 带参数执行(数组参数)
  • ReflectionMethod::isAbstract — 判断方法是否是抽象方法
  • ReflectionMethod::isConstructor — 判断方法是否是构造方法
  • ReflectionMethod::isDestructor — 判断方法是否是析构方法
  • ReflectionMethod::isFinal — 判断方法是否定义 final
  • ReflectionMethod::isPrivate — 判断方法是否是私有方法
  • ReflectionMethod::isProtected — 判断方法是否是保护方法 (protected)
  • ReflectionMethod::isPublic — 判断方法是否是公开方法
  • ReflectionMethod::isStatic — 判断方法是否是静态方法
  • ReflectionMethod::setAccessible — 设置方法是否访问
<?php


/**
 * Class Demo2
 * @Author Mustard
 * @Date 2020-11-27
 */
class Demo2 {
    public static function test1($args1, $args2 = 'boy') {
        echo 'hello ' . $args1 . ', I am a static ' . $args2 . PHP_EOL;
    }

    public function test2($args1, $args2 = 'boy') {
        echo 'hello ' . $args1 . ', I am a ' . $args2 . PHP_EOL;
    }

    private function test3($args1) {
        echo 'hello ' . $args1 . PHP_EOL;
    }

    public function test4($a1, $a2, $a3) {
        print_r("$a1 + $a2 + $a3 = " . ($a1 + $a2 + $a3) . PHP_EOL);
    }

}


/*************静态方法测试************/
$reflectionStaticMethod = new ReflectionMethod('Demo2', 'test1');
echo '参数个数为:' . $reflectionStaticMethod->getNumberOfParameters() . PHP_EOL; // 参数个数为:2
echo '必填参数个数为:' . $reflectionStaticMethod->getNumberOfRequiredParameters() . PHP_EOL; // 必填参数个数为:1
echo '是否是公开方法:' . ($reflectionStaticMethod->isPublic() ? '是' : '否') . PHP_EOL;  // 是否是公开方法:是
echo '是否是私有方法:' . ($reflectionStaticMethod->isPrivate() ? '是' : '否') . PHP_EOL; // 是否是公开方法:否

// 如果调用的是静态方法,第一个参数(object)填写null
$reflectionStaticMethod->invoke(null, 'mustard');  //hello mustard, I am a static boy

/*************成员方法测试************/
$reflectionMethod = new ReflectionMethod('Demo2', 'test2');
$reflectionClass = new ReflectionClass('Demo2');
$newInstance = $reflectionClass->newInstance(); // 需要去弄一个实例 或者是直接new Demo2();
$reflectionMethod->invoke($newInstance, 'mustard');  //hello mustard, I am a boy

/*************多参数方法测试************/
$reflectionMethod = new ReflectionMethod('Demo2', 'test4');
$reflectionMethod->invoke($newInstance, 1, 2, 3);                // 1 + 2 + 3 = 6
$reflectionMethod->invokeArgs($newInstance, [1, 2, 3]);        // 1 + 2 + 3 = 6


/*************成员私有方法测试************/
$reflectionPrivateMethod = new ReflectionMethod('Demo2', 'test3');
echo '是否是私有方法:' . ($reflectionPrivateMethod->isPrivate() ? '是' : '否') . PHP_EOL; // 是否是公开方法:否
try {
    $reflectionPrivateMethod->invoke($newInstance, 'mustard');  // 异常
} catch (ReflectionException $e) {
    print_r($e->getMessage()); // Trying to invoke private method Demo2::test3() from scope ReflectionMethod
}

封装一个方法,输入类名、方法名及参数就可以执行:

/**
 * @param string $className 类名(全类名)
 * @param string $methodName 方法名
 * @param array $args 参数 可选
 * @return bool|mixed 返回结果,执行失败返回false
 */
function myInvoke($className, $methodName, $args = []) {
    try {
        $reflectionClass = new ReflectionClass($className);
        $newInstance = $reflectionClass->newInstance();
        $reflectionMethod = new ReflectionMethod($className, $methodName);
        return $reflectionMethod->invokeArgs($newInstance, $args);
    } catch (ReflectionException $e) {
        print_r('ReflectionException: ' . $e->getMessage());
        return false;
    }
}

myInvoke('Demo2', 'test4', [1, 2, 3]);

2.项目应用

2.1.策略者模式(Strategy)

项目背景:某公园有多个售票窗口,每个窗口售票的类型不同,有不同的打折方式,如:学生票八折,VIP票五折等。通过配置文件来决定窗口类型。

Ticket.php

<?php


class Ticket {
    private $price;
    private $discount;

    public function __construct($price) {
        $this->price = $price;
    }

    public function getPrice() {
        if (empty($this->discount))
            throw new Exception('请先初始化折扣属性');
        echo '原始票价为:' . $this->price . PHP_EOL;
        /*
        // 使用反射调用方法
        $reflectionMethod = new ReflectionMethod($this->discount, 'calculate');
        return $reflectionMethod->invoke($this->discount, $this->price);
        */

        // 正常调用
        return $this->discount->calculate($this->price);
    }

    public function setDiscount($discount) {
        $this->discount = $discount;
    }

}

Discount.php(抽象接口)

<?php

interface Discount {
    public function calculate($price);
}

StudentDiscount.php(具体打折实现类)

<?php

class StudentDiscount implements Discount {
    public function calculate($price) {
        return $price * 0.8;
    }
}

VIPDiscount.php(具体打折实现类)

<?php

class StudentDiscount implements Discount {
    public function calculate($price) {
        return $price * 0.8;
    }
}

Client.php(客户端)

<?php

class Client {
    public function sale($discount) {
        print_r('sale');
        $ticket = new Ticket(100.00);
        $ticket->setDiscount($discount);
        print_r('打折后票价为:' . $ticket->getPrice() . PHP_EOL);
    }
}

config.ini(配置文件)

[system]
type_a = StudentDiscount
type_b = VIPDiscount

Test.php(测试文件)

<?php
/**
 * 自动加载
 * @param $class_name
 */
function __autoload($className) {
    require './' . $className . '.php';
}

try {
    $config = parse_ini_file('./config.ini');
    $type = $config['type_a'];
    $discount = (new ReflectionClass($type))->newInstance();
    $client = new Client();
    $client->sale($discount);

    print_r('*******************' . PHP_EOL);

    $type = $config['type_b'];
    $discount = (new ReflectionClass($type))->newInstance();
    $client = new Client();
    $client->sale($discount);
} catch (Exception $e) {
    print_r($e->getMessage());
}


/*
 输出内容:
    sale原始票价为:100
    打折后票价为:80
    *******************
    sale原始票价为:100
    打折后票价为:50
 */

参考文章:
【PHP官方手册】https://www.php.net/manual/zh/book.reflection.php
【空城里的往日时光】https://www.cnblogs.com/llljpf/p/10830651.html
【晏南风i】https://www.php.cn/blog/detail/9327.html

扫码在手机查看