Inhaltsverzeichnis
一点历史
一个简单的例子
对象成员信息
操作实例数据
间接反射的使用
回调
使用变量的值
我们什么时候应该使用反射?
最终想法
Heim Backend-Entwicklung PHP-Tutorial Reflexionsmechanismus in PHP

Reflexionsmechanismus in PHP

Aug 31, 2023 pm 01:57 PM
php 反射 反射机制

反射通常被定义为程序在执行时检查自身并修改其逻辑的能力。用不太专业的术语来说,反射是要求一个对象告诉您它的属性和方法,并更改这些成员(甚至是私有成员)。在本课程中,我们将深入探讨如何实现这一点,以及它何时可能有用。


一点历史

在编程时代的初期,出现了汇编语言。用汇编语言编写的程序驻留在计算机内部的物理寄存器中。通过读取寄存器可以随时检查其组成、方法和值。更重要的是,您可以在程序运行时通过简单地修改这些寄存器来更改程序。它需要对正在运行的程序有一些深入的了解,但它本质上是反思性的。

与任何很酷的玩具一样,使用反射,但不要滥用它。

随着高级编程语言(如 C)的出现,这种反射性逐渐消失。后来它通过面向对象编程重新引入。

如今,大多数编程语言都可以使用反射。静态类型语言(例如 Java)在反射方面几乎没有问题。然而,我发现有趣的是,任何动态类型语言(如 PHP 或 Ruby)都很大程度上基于反射。如果没有反射的概念,鸭子类型很可能无法实现。当您将一个对象发送到另一个对象(例如参数)时,接收对象无法知道该对象的结构和类型。它所能做的就是使用反射来识别可以在接收到的对象上调用和不能调用的方法。


一个简单的例子

反射在 PHP 中很普遍。事实上,有几种情况您可能会在不知情的情况下使用它。例如:

// Nettuts.php

require_once 'Editor.php';

class Nettuts {

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

}
Nach dem Login kopieren

还有:

// 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;
	}

}
Nach dem Login kopieren

在此代码中,我们直接调用具有已知类型的本地初始化变量。在 publishNextArticle() 中创建编辑器,可以明显看出 $editor 变量的类型为 Editor。这里不需要反射,但是我们引入一个新类,名为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);
		}
	}

}
Nach dem Login kopieren

接下来,修改 Nettuts,如下所示:

// Nettuts.php

class Nettuts {

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

}
Nach dem Login kopieren

现在,NettutsEditor 类完全没有关系。它不包含它的文件,它不初始化它的类,它甚至不知道它的存在。我可以将任何类型的对象传递到 publishNextArticle() 方法中,代码就可以工作。

Reflexionsmechanismus in PHP

从这个类图中可以看到,Nettuts只与Manager有直接关系。 Manager 创建它,因此 Manager 依赖于 Nettuts。但是 Nettuts 不再与 Editor 类有任何关系,并且 Editor 仅与 Manager 相关。

在运行时,Nettuts 使用 Editor 对象,因此有 <> 和问号。在运行时,PHP 检查接收到的对象并验证它是否实现了 setNextArticle()publish() 方法。

对象成员信息

我们可以让 PHP 显示对象的详细信息。让我们创建一个 PHPUnit 测试来帮助我们轻松地测试我们的代码:

// 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);
	}

}
Nach dem Login kopieren

现在,将 var_dump() 添加到 Nettuts:

// Nettuts.php

class NetTuts {

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

}
Nach dem Login kopieren

运行测试,并观察输出中发生的奇迹:

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)
Nach dem Login kopieren

我们的反射类有一个 name 属性,设置为 $editor 变量的原始类型:Editor,但这并不是太多信息。 Editor 的方法怎么样?

// Nettuts.php

class Nettuts {

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

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

}
Nach dem Login kopieren

在此代码中,我们将反射类的实例分配给 $reflector 变量,以便我们现在可以触发其方法。 ReflectionClass 公开了大量可用于获取对象信息的方法。其中一个方法是 getMethods(),它返回一个包含每个方法信息的数组。

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)
Nach dem Login kopieren

另一个方法,getProperties(),检索对象的属性(甚至是私有属性!):

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)
Nach dem Login kopieren

getMethod()getProperties() 返回的数组中的元素分别为 ReflectionMethodReflectionProperty 类型;这些对象非常有用:

// 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()
	}

}
Nach dem Login kopieren

这里,我们使用 getMethod() 来检索名称为“publish”的单个方法;其结果是 ReflectionMethod 对象。然后,我们调用 invoke() 方法,并向其传递 $editor 对象,以便再次执行编辑器的 publish() 方法。

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

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

// Editor.php

class Editor {

	[ ... ]

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

}
Nach dem Login kopieren

新的输出:

PHPUnit 3.6.11 by Sebastian Bergmann.

.HERE
HERE

Time: 0 seconds, Memory: 2.25Mb

OK (1 test, 0 assertions)
Nach dem Login kopieren

操作实例数据

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

// Editor.php

class Editor {

	private $name;
	public $articleId;

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

	[ ... ]

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

}
Nach dem Login kopieren

这个新方法被称为 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);

	}

}
Nach dem Login kopieren

尽管这会在 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.
Nach dem Login kopieren

为了解决这个问题,我们需要请求 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));
	}

}
Nach dem Login kopieren

调用 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)
Nach dem Login kopieren

如您所见,我们已成功读取私有变量。第一行输出来自对象自己的 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));
	}

}
Nach dem Login kopieren

就是这样。此代码将“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)
Nach dem Login kopieren

间接反射的使用

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')));
	}

}
Nach dem Login kopieren

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

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)
Nach dem Login kopieren

使用变量的值

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

// 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());
	}

}
Nach dem Login kopieren

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


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

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

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

最终想法

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

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

感谢您的阅读!

Das obige ist der detaillierte Inhalt vonReflexionsmechanismus in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
2 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Repo: Wie man Teamkollegen wiederbelebt
1 Monate vor By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Abenteuer: Wie man riesige Samen bekommt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

CakePHP-Projektkonfiguration CakePHP-Projektkonfiguration Sep 10, 2024 pm 05:25 PM

In diesem Kapitel werden wir die Umgebungsvariablen, die allgemeine Konfiguration, die Datenbankkonfiguration und die E-Mail-Konfiguration in CakePHP verstehen.

PHP 8.4 Installations- und Upgrade-Anleitung für Ubuntu und Debian PHP 8.4 Installations- und Upgrade-Anleitung für Ubuntu und Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 bringt mehrere neue Funktionen, Sicherheitsverbesserungen und Leistungsverbesserungen mit einer beträchtlichen Menge an veralteten und entfernten Funktionen. In dieser Anleitung wird erklärt, wie Sie PHP 8.4 installieren oder auf PHP 8.4 auf Ubuntu, Debian oder deren Derivaten aktualisieren. Obwohl es möglich ist, PHP aus dem Quellcode zu kompilieren, ist die Installation aus einem APT-Repository wie unten erläutert oft schneller und sicherer, da diese Repositorys in Zukunft die neuesten Fehlerbehebungen und Sicherheitsupdates bereitstellen.

CakePHP Datum und Uhrzeit CakePHP Datum und Uhrzeit Sep 10, 2024 pm 05:27 PM

Um in cakephp4 mit Datum und Uhrzeit zu arbeiten, verwenden wir die verfügbare FrozenTime-Klasse.

CakePHP-Datei hochladen CakePHP-Datei hochladen Sep 10, 2024 pm 05:27 PM

Um am Datei-Upload zu arbeiten, verwenden wir den Formular-Helfer. Hier ist ein Beispiel für den Datei-Upload.

CakePHP-Routing CakePHP-Routing Sep 10, 2024 pm 05:25 PM

In diesem Kapitel lernen wir die folgenden Themen im Zusammenhang mit dem Routing kennen.

Besprechen Sie CakePHP Besprechen Sie CakePHP Sep 10, 2024 pm 05:28 PM

CakePHP ist ein Open-Source-Framework für PHP. Es soll die Entwicklung, Bereitstellung und Wartung von Anwendungen erheblich vereinfachen. CakePHP basiert auf einer MVC-ähnlichen Architektur, die sowohl leistungsstark als auch leicht zu verstehen ist. Modelle, Ansichten und Controller gu

So richten Sie Visual Studio-Code (VS-Code) für die PHP-Entwicklung ein So richten Sie Visual Studio-Code (VS-Code) für die PHP-Entwicklung ein Dec 20, 2024 am 11:31 AM

Visual Studio Code, auch bekannt als VS Code, ist ein kostenloser Quellcode-Editor – oder eine integrierte Entwicklungsumgebung (IDE) –, die für alle gängigen Betriebssysteme verfügbar ist. Mit einer großen Sammlung von Erweiterungen für viele Programmiersprachen kann VS Code c

CakePHP erstellt Validatoren CakePHP erstellt Validatoren Sep 10, 2024 pm 05:26 PM

Der Validator kann durch Hinzufügen der folgenden zwei Zeilen im Controller erstellt werden.

See all articles