PHP의 반사 메커니즘
리플렉션은 일반적으로 프로그램이 실행 중에 자체를 검사하고 논리를 수정하는 기능으로 정의됩니다. 덜 기술적인 용어로 말하면, 리플렉션은 객체에 속성과 메서드를 알려달라고 요청하고 해당 멤버(비공개 멤버도 포함)를 변경하는 것입니다. 이 과정에서는 이를 수행하는 방법과 이것이 언제 유용할 수 있는지 자세히 살펴보겠습니다.
약간의 역사
프로그래밍 시대 초에 어셈블리 언어가 등장했습니다. 어셈블리 언어로 작성된 프로그램은 컴퓨터 내부의 물리적 레지스터에 상주합니다. 그 구성, 방법, 값은 레지스터를 읽어 언제든지 확인할 수 있습니다. 더욱이, 프로그램이 실행되는 동안 간단히 이러한 레지스터를 수정하여 프로그램을 변경할 수 있습니다. 실행 중인 프로그램에 대한 깊은 지식이 필요하지만 본질적으로 반영적입니다.
다른 멋진 장난감과 마찬가지로 반사를 사용하되 남용하지 마세요.
C와 같은 고급 프로그래밍 언어의 출현으로 이러한 재귀성은 점차 사라졌습니다. 나중에 객체 지향 프로그래밍을 통해 다시 도입되었습니다.
Reflection은 요즘 대부분의 프로그래밍 언어에서 사용할 수 있습니다. Java와 같은 정적으로 유형이 지정된 언어는 리플렉션에 문제가 거의 없습니다. 그러나 PHP나 Ruby와 같은 동적 유형 언어가 리플렉션에 크게 기반을 두고 있다는 점이 흥미롭습니다. 반사라는 개념이 없다면 오리 타이핑은 불가능할 것입니다. 개체를 다른 개체(예: 매개 변수)로 보낼 때 수신 개체는 개체의 구조와 유형을 알 수 없습니다. 할 수 있는 일은 리플렉션을 사용하여 수신된 개체에 대해 호출할 수 있는 메서드와 호출할 수 없는 메서드를 식별하는 것뿐입니다.
간단한 예
반사(Reflection)는 PHP에서 일반적입니다. 실제로, 자신도 모르게 이 기능을 사용하고 있는 상황이 여러 가지 있습니다. 예:
으아악또한:
으아악이 코드에서는 알려진 유형으로 초기화된 로컬 변수를 직접 호출합니다. publishNextArticle()
中创建编辑器,可以明显看出 $editor
变量的类型为 Editor
。这里不需要反射,但是我们引入一个新类,名为Manager
에서:
다음으로 Nettuts
를 다음과 같이 수정하세요.
이제 Nettuts
与 Editor
类完全没有关系。它不包含它的文件,它不初始化它的类,它甚至不知道它的存在。我可以将任何类型的对象传递到 publishNextArticle()
는 Editor
클래스와 전혀 관련이 없습니다. 파일을 포함하지 않고, 클래스를 초기화하지 않으며, 클래스가 존재하는지조차 모릅니다. 모든 유형의 개체를 publishNextArticle()
메서드에 전달할 수 있으며 코드가 작동합니다.
이 클래스 다이어그램에서 볼 수 있듯이 Nettuts
只与Manager
有直接关系。 Manager
创建它,因此 Manager
依赖于 Nettuts
。但是 Nettuts
不再与 Editor
类有任何关系,并且 Editor
仅与 Manager
는 Manager
에만 직접적으로 관련되어 있습니다. Manager
가 생성하므로 Manager
는
더 이상 Editor
클래스와 관련이 없으며 Editor
는 Manager
에만 관련됩니다. Nettuts
使用 Editor
对象,因此有 > 和问号。在运行时,PHP 检查接收到的对象并验证它是否实现了 setNextArticle()
和 publish()
는 Editor
개체를 사용하므로 >와 물음표가 표시됩니다. 런타임 시 PHP는 수신된 객체를 검사하고 setNextArticle()
및 publish()
메서드를 구현하는지 확인합니다.
객체 회원정보
PHP에서 객체의 세부정보를 표시하도록 할 수 있습니다. 코드를 쉽게 테스트하는 데 도움이 되는 PHPUnit 테스트를 만들어 보겠습니다. var_dump()
添加到 Nettuts
으아악
:
에var_dump()
를 추가하세요.
으아악
테스트를 실행하고 출력에서 마법 같은 일이 일어나는지 확인하세요. name
属性,设置为 $editor
变量的原始类型:Editor
,但这并不是太多信息。 Editor
으아악
메서드가 있는 게 어때요? $reflector
变量,以便我们现在可以触发其方法。 ReflectionClass
公开了大量可用于获取对象信息的方法。其中一个方法是 getMethods()
으아악
에 할당합니다. getProperties()
으아악
는 객체의 속성(비공개 속성도 포함)을 검색합니다. getMethod()
和 getProperties()
返回的数组中的元素分别为 ReflectionMethod
和 ReflectionProperty
으아악
getMethod()
및 에서 반환된 배열의 요소는 각각 ReflectionMethod
및 ReflectionProperty
유형입니다. 이러한 개체는 매우 유용합니다.
으아악
getMethod()
来检索名称为“publish”的单个方法;其结果是 ReflectionMethod
对象。然后,我们调用 invoke()
方法,并向其传递 $editor
对象,以便再次执行编辑器的 publish()
여기서는
在我们的例子中,这个过程很简单,因为我们已经有一个 Editor
对象传递给 invoke()
。在某些情况下,我们可能有多个 Editor
对象,这使我们可以自由选择使用哪个对象。在其他情况下,我们可能没有可以使用的对象,在这种情况下,我们需要从 ReflectionClass
获取一个对象。
我们来修改Editor
的publish()
方法来演示双重调用:
// Editor.php class Editor { [ ... ] public function publish() { // publish logic goes here echo ("HERE\n"); return true; } }
新的输出:
PHPUnit 3.6.11 by Sebastian Bergmann. .HERE HERE Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
操作实例数据
我们还可以在执行时修改代码。修改没有公共设置器的私有变量怎么样?让我们向 Editor
添加一个方法来检索编辑器的名称:
// Editor.php class Editor { private $name; public $articleId; function __construct($name) { $this->name = $name; } [ ... ] function getEditorName() { return $this->name; } }
这个新方法被称为 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); } }
尽管这会在 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.
为了解决这个问题,我们需要请求 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)); } }
调用 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)
如您所见,我们已成功读取私有变量。第一行输出来自对象自己的 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)); } }
就是这样。此代码将“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)
间接反射的使用
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'))); } }
以下输出表明代码检索了正确的值:
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)
使用变量的值
间接反射的另一个示例是通过变量中包含的值来调用方法,而不是直接调用它。例如:
// 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()); } }
此代码产生与前面示例相同的输出。 PHP 只是用它所代表的字符串替换该变量并调用该方法。当您想通过使用类名变量来创建对象时,它甚至可以工作。
我们什么时候应该使用反射?
现在我们已经把技术细节抛在脑后了,我们什么时候应该利用反射呢?以下是一些场景:
- 动态类型如果没有反射可能是不可能的。
- 面向方面的编程侦听方法调用并将代码放置在方法周围,所有这些都通过反射完成。
- PHPUnit 与其他模拟框架一样,严重依赖反射。
- Web 框架通常将反射用于不同的目的。有些人用它来初始化模型、构建视图对象等等。 Laravel 大量使用反射来注入依赖项。
- 元编程,就像我们的最后一个例子一样,是隐藏的反射。
- 代码分析框架使用反射来理解您的代码。
最终想法
与任何很酷的玩具一样,使用反射,但不要滥用它。当您检查许多对象时,反射的成本很高,并且有可能使项目的架构和设计变得复杂。我建议您仅在它确实为您带来优势或没有其他可行选择时才使用它。
就我个人而言,我只在少数情况下使用过反射,最常见的是在使用缺乏文档的第三方模块时。我发现自己经常使用与上一个示例类似的代码。当您的 MVC 使用包含“添加”或“删除”值的变量进行响应时,调用正确的方法很容易。
感谢您的阅读!
위 내용은 PHP의 반사 메커니즘의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











PHP 8.4는 상당한 양의 기능 중단 및 제거를 통해 몇 가지 새로운 기능, 보안 개선 및 성능 개선을 제공합니다. 이 가이드에서는 Ubuntu, Debian 또는 해당 파생 제품에서 PHP 8.4를 설치하거나 PHP 8.4로 업그레이드하는 방법을 설명합니다.

CakePHP는 PHP용 오픈 소스 프레임워크입니다. 이는 애플리케이션을 훨씬 쉽게 개발, 배포 및 유지 관리할 수 있도록 하기 위한 것입니다. CakePHP는 강력하고 이해하기 쉬운 MVC와 유사한 아키텍처를 기반으로 합니다. 모델, 뷰 및 컨트롤러 gu

CakePHP에 로그인하는 것은 매우 쉬운 작업입니다. 한 가지 기능만 사용하면 됩니다. cronjob과 같은 백그라운드 프로세스에 대해 오류, 예외, 사용자 활동, 사용자가 취한 조치를 기록할 수 있습니다. CakePHP에 데이터를 기록하는 것은 쉽습니다. log() 함수는 다음과 같습니다.

VS Code라고도 알려진 Visual Studio Code는 모든 주요 운영 체제에서 사용할 수 있는 무료 소스 코드 편집기 또는 통합 개발 환경(IDE)입니다. 다양한 프로그래밍 언어에 대한 대규모 확장 모음을 통해 VS Code는

CakePHP는 오픈 소스 MVC 프레임워크입니다. 이를 통해 애플리케이션 개발, 배포 및 유지 관리가 훨씬 쉬워집니다. CakePHP에는 가장 일반적인 작업의 과부하를 줄이기 위한 여러 라이브러리가 있습니다.
