Table of Contents
A little history
A simple example
Object member information
操作实例数据
间接反射的使用
回调
使用变量的值
我们什么时候应该使用反射?
最终想法
Home Backend Development PHP Tutorial Reflection mechanism in PHP

Reflection mechanism in PHP

Aug 31, 2023 pm 01:57 PM
php reflection reflection mechanism

Reflection is generally defined as the ability of a program to examine itself and modify its logic while executing. In less technical terms, reflection is asking an object to tell you its properties and methods, and changing those members (even private members). In this course, we'll take a closer look at how to accomplish this, and when it might be useful.


A little history

In the early days of the programming era, assembly language appeared. Programs written in assembly language reside in physical registers inside the computer. Its composition, methods and values ​​can be checked at any time by reading the registers. What's more, you can change your program by simply modifying these registers while it's running. It requires some deep knowledge of the running program, but it is reflective in nature.

As with any cool toy, use reflection, but don't abuse it.

With the advent of high-level programming languages ​​(such as C), this reflexivity gradually disappeared. Later it was reintroduced through object-oriented programming.

Reflection is available in most programming languages ​​these days. Staticly typed languages ​​such as Java have few problems with reflection. However, I find it interesting that any dynamically typed language like PHP or Ruby is heavily based on reflection. Without the concept of reflection, duck typing would most likely not be possible. When you send an object to another object (such as a parameter), the receiving object has no way of knowing the object's structure and type. All it can do is use reflection to identify methods that can and cannot be called on the received object.


A simple example

Reflection is common in PHP. In fact, there are several situations where you might be using it without knowing it. For example:

// Nettuts.php

require_once 'Editor.php';

class Nettuts {

	function publishNextArticle() {
		$editor = new Editor('John Doe');
		$editor->setNextArticle('135523');
		$editor->publish();
	}

}
Copy after login

besides:

// Editor.php

class Editor {

	private $name;
	public $articleId;

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

	public function setNextArticle($articleId) {
		$this->articleId = $articleId;
	}

	public function publish() {
		// publish logic goes here
		return true;
	}

}
Copy after login

In this code, we directly call a local initialized variable with a known type. When creating the editor in publishNextArticle(), it can be clearly seen that the type of the $editor variable is Editor. There is no need for reflection here, but we introduce a new class named Manager:

// Manager.php

require_once './Editor.php';
require_once './Nettuts.php';

class Manager {

	function doJobFor(DateTime $date) {
		if ((new DateTime())->getTimestamp() > $date->getTimestamp()) {
			$editor = new Editor('John Doe');
			$nettuts = new Nettuts();
			$nettuts->publishNextArticle($editor);
		}
	}

}
Copy after login

Next, modify Nettuts as follows:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		$editor->setNextArticle('135523');
		$editor->publish();
	}

}
Copy after login

Now, Nettuts has absolutely nothing to do with the Editor class. It doesn't contain its files, it doesn't initialize its classes, it doesn't even know it exists. I can pass any type of object into the publishNextArticle() method and the code works.

Reflection mechanism in PHP

As you can see from this class diagram, Nettuts is only directly related to Manager. Manager creates it so Manager depends on Nettuts. But Nettuts no longer has anything to do with the Editor class, and Editor is only relevant to Manager.

At runtime, Nettuts uses the Editor object, hence the <> and the question mark. At runtime, PHP examines the received object and verifies that it implements the setNextArticle() and publish() methods.

Object member information

We can let PHP display the detailed information of the object. Let's create a PHPUnit test to help us easily test our code:

// ReflectionTest.php

require_once '../Editor.php';
require_once '../Nettuts.php';

class ReflectionTest extends PHPUnit_Framework_TestCase {

	function testItCanReflect() {
		$editor = new Editor('John Doe');
		$tuts = new Nettuts();
		$tuts->publishNextArticle($editor);
	}

}
Copy after login

Now, add var_dump() to Nettuts:

// Nettuts.php

class NetTuts {

	function publishNextArticle($editor) {
		$editor->setNextArticle('135523');
		$editor->publish();
		var_dump(new ReflectionClass($editor));
	}

}
Copy after login

Run the test and watch the magic happen in the output:

PHPUnit 3.6.11 by Sebastian Bergmann.

.object(ReflectionClass)#197 (1) {
  ["name"]=>
  string(6) "Editor"
}


Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

Our reflection class has a name property set to the original type of the $editor variable: Editor, but that's not too much information. How about the Editor method?

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		$editor->setNextArticle('135523');
		$editor->publish();

		$reflector = new ReflectionClass($editor);
		var_dump($reflector->getMethods());
	}

}
Copy after login

In this code, we assign an instance of the reflection class to the $reflector variable so that we can now trigger its methods. ReflectionClass Exposes a number of methods that can be used to obtain information about an object. One of these methods is getMethods(), which returns an array containing information about each method.

PHPUnit 3.6.11 by Sebastian Bergmann.

.array(3) {
  [0]=>
  &object(ReflectionMethod)#196 (2) {
    ["name"]=>
    string(11) "__construct"
    ["class"]=>
    string(6) "Editor"
  }
  [1]=>
  &object(ReflectionMethod)#195 (2) {
    ["name"]=>
    string(14) "setNextArticle"
    ["class"]=>
    string(6) "Editor"
  }
  [2]=>
  &object(ReflectionMethod)#194 (2) {
    ["name"]=>
    string(7) "publish"
    ["class"]=>
    string(6) "Editor"
  }
}

Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

Another method, getProperties(), retrieves the properties of the object (even private properties!):

PHPUnit 3.6.11 by Sebastian Bergmann.

.array(2) {
  [0]=>
  &object(ReflectionProperty)#196 (2) {
    ["name"]=>
    string(4) "name"
    ["class"]=>
    string(6) "Editor"
  }
  [1]=>
  &object(ReflectionProperty)#195 (2) {
    ["name"]=>
    string(9) "articleId"
    ["class"]=>
    string(6) "Editor"
  }
}

Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

The elements in the arrays returned from getMethod() and getProperties() are of type ReflectionMethod and ReflectionProperty respectively; these objects very useful:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		$editor->setNextArticle('135523');
		$editor->publish(); // first call to publish()

		$reflector = new ReflectionClass($editor);
		$publishMethod = $reflector->getMethod('publish');
		$publishMethod->invoke($editor); // second call to publish()
	}

}
Copy after login

Here, we use getMethod() to retrieve a single method named "publish"; the result is a ReflectionMethod object. We then call the invoke() method, passing it the $editor object to execute the editor's publish() method again.

在我们的例子中,这个过程很简单,因为我们已经有一个 Editor 对象传递给 invoke()。在某些情况下,我们可能有多个 Editor 对象,这使我们可以自由选择使用哪个对象。在其他情况下,我们可能没有可以使用的对象,在这种情况下,我们需要从 ReflectionClass 获取一个对象。

我们来修改Editorpublish()方法来演示双重调用:

// Editor.php

class Editor {

	[ ... ]

	public function publish() {
		// publish logic goes here
		echo ("HERE\n");
		return true;
	}

}
Copy after login

新的输出:

PHPUnit 3.6.11 by Sebastian Bergmann.

.HERE
HERE

Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

操作实例数据

我们还可以在执行时修改代码。修改没有公共设置器的私有变量怎么样?让我们向 Editor 添加一个方法来检索编辑器的名称:

// Editor.php

class Editor {

	private $name;
	public $articleId;

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

	[ ... ]

	function getEditorName() {
		return $this->name;
	}

}
Copy after login

这个新方法被称为 getEditorName(),并且仅返回私有 $name 变量的值。 $name 变量是在创建时设置的,我们没有公共方法可以让我们更改它。但我们可以使用反射来访问这个变量。您可能首先尝试更明显的方法:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		var_dump($editor->getEditorName());

		$reflector = new ReflectionClass($editor);
		$editorName = $reflector->getProperty('name');
		$editorName->getValue($editor);

	}

}
Copy after login

尽管这会在 var_dump() 行输出值,但在尝试通过反射检索该值时会引发错误:

PHPUnit 3.6.11 by Sebastian Bergmann.

Estring(8) "John Doe"


Time: 0 seconds, Memory: 2.50Mb

There was 1 error:

1) ReflectionTest::testItCanReflect
ReflectionException: Cannot access non-public member Editor::name

[...]/Reflection in PHP/Source/NetTuts.php:13
[...]/Reflection in PHP/Source/Tests/ReflectionTest.php:13
/usr/bin/phpunit:46

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
Copy after login

为了解决这个问题,我们需要请求 ReflectionProperty 对象授予我们访问私有变量和方法的权限:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		var_dump($editor->getEditorName());

		$reflector = new ReflectionClass($editor);
		$editorName = $reflector->getProperty('name');
		$editorName->setAccessible(true);
		var_dump($editorName->getValue($editor));
	}

}
Copy after login

调用 setAccessible() 并传递 true 可以解决问题:

PHPUnit 3.6.11 by Sebastian Bergmann.

.string(8) "John Doe"
string(8) "John Doe"


Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

如您所见,我们已成功读取私有变量。第一行输出来自对象自己的 getEditorName() 方法,第二行来自反射。但是改变私有变量的值又如何呢?使用 setValue() 方法:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		var_dump($editor->getEditorName());

		$reflector = new ReflectionClass($editor);
		$editorName = $reflector->getProperty('name');
		$editorName->setAccessible(true);
		$editorName->setValue($editor, 'Mark Twain');
		var_dump($editorName->getValue($editor));
	}

}
Copy after login

就是这样。此代码将“John Doe”更改为“Mark Twain”。

PHPUnit 3.6.11 by Sebastian Bergmann.

.string(8) "John Doe"
string(10) "Mark Twain"


Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

间接反射的使用

PHP 的一些内置功能间接使用反射,其中一个是 call_user_func() 函数。

回调

call_user_func() 函数接受一个数组:第一个元素指向对象,第二个元素指向方法的名称。您可以提供一个可选参数,然后将其传递给被调用的方法。例如:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		var_dump($editor->getEditorName());

		$reflector = new ReflectionClass($editor);
		$editorName = $reflector->getProperty('name');
		$editorName->setAccessible(true);
		$editorName->setValue($editor, 'Mark Twain');
		var_dump($editorName->getValue($editor));

		var_dump(call_user_func(array($editor, 'getEditorName')));
	}

}
Copy after login

以下输出表明代码检索了正确的值:

PHPUnit 3.6.11 by Sebastian Bergmann.

.string(8) "John Doe"
string(10) "Mark Twain"
string(10) "Mark Twain"


Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Copy after login

使用变量的值

间接反射的另一个示例是通过变量中包含的值来调用方法,而不是直接调用它。例如:

// Nettuts.php

class Nettuts {

	function publishNextArticle($editor) {
		var_dump($editor->getEditorName());

		$reflector = new ReflectionClass($editor);
		$editorName = $reflector->getProperty('name');
		$editorName->setAccessible(true);
		$editorName->setValue($editor, 'Mark Twain');
		var_dump($editorName->getValue($editor));

		$methodName = 'getEditorName';
		var_dump($editor->$methodName());
	}

}
Copy after login

此代码产生与前面示例相同的输出。 PHP 只是用它所代表的字符串替换该变量并调用该方法。当您想通过使用类名变量来创建对象时,它甚至可以工作。


我们什么时候应该使用反射?

现在我们已经把技术细节抛在脑后了,我们什么时候应该利用反射呢?以下是一些场景:

  • 动态类型如果没有反射可能是不可能的。
  • 面向方面的编程侦听方法调用并将代码放置在方法周围,所有这些都通过反射完成。
  • PHPUnit 与其他模拟框架一样,严重依赖反射。
  • Web 框架通常将反射用于不同的目的。有些人用它来初始化模型、构建视图对象等等。 Laravel 大量使用反射来注入依赖项。
  • 元编程,就像我们的最后一个例子一样,是隐藏的反射。
  • 代码分析框架使用反射来理解您的代码。

最终想法

与任何很酷的玩具一样,使用反射,但不要滥用它。当您检查许多对象时,反射的成本很高,并且有可能使项目的架构和设计变得复杂。我建议您仅在它确实为您带来优势或没有其他可行选择时才使用它。

就我个人而言,我只在少数情况下使用过反射,最常见的是在使用缺乏文档的第三方模块时。我发现自己经常使用与上一个示例类似的代码。当您的 MVC 使用包含“添加”或“删除”值的变量进行响应时,调用正确的方法很容易。

感谢您的阅读!

The above is the detailed content of Reflection mechanism in PHP. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 brings several new features, security improvements, and performance improvements with healthy amounts of feature deprecations and removals. This guide explains how to install PHP 8.4 or upgrade to PHP 8.4 on Ubuntu, Debian, or their derivati

How To Set Up Visual Studio Code (VS Code) for PHP Development How To Set Up Visual Studio Code (VS Code) for PHP Development Dec 20, 2024 am 11:31 AM

Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems. With a large collection of extensions for many programming languages, VS Code can be c

7 PHP Functions I Regret I Didn't Know Before 7 PHP Functions I Regret I Didn't Know Before Nov 13, 2024 am 09:42 AM

If you are an experienced PHP developer, you might have the feeling that you’ve been there and done that already.You have developed a significant number of applications, debugged millions of lines of code, and tweaked a bunch of scripts to achieve op

How do you parse and process HTML/XML in PHP? How do you parse and process HTML/XML in PHP? Feb 07, 2025 am 11:57 AM

This tutorial demonstrates how to efficiently process XML documents using PHP. XML (eXtensible Markup Language) is a versatile text-based markup language designed for both human readability and machine parsing. It's commonly used for data storage an

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

PHP Program to Count Vowels in a String PHP Program to Count Vowels in a String Feb 07, 2025 pm 12:12 PM

A string is a sequence of characters, including letters, numbers, and symbols. This tutorial will learn how to calculate the number of vowels in a given string in PHP using different methods. The vowels in English are a, e, i, o, u, and they can be uppercase or lowercase. What is a vowel? Vowels are alphabetic characters that represent a specific pronunciation. There are five vowels in English, including uppercase and lowercase: a, e, i, o, u Example 1 Input: String = "Tutorialspoint" Output: 6 explain The vowels in the string "Tutorialspoint" are u, o, i, a, o, i. There are 6 yuan in total

Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? Apr 03, 2025 am 12:03 AM

What are the magic methods of PHP? PHP's magic methods include: 1.\_\_construct, used to initialize objects; 2.\_\_destruct, used to clean up resources; 3.\_\_call, handle non-existent method calls; 4.\_\_get, implement dynamic attribute access; 5.\_\_set, implement dynamic attribute settings. These methods are automatically called in certain situations, improving code flexibility and efficiency.

See all articles