PHP 자동 로드 메커니즘에 대한 자세한 설명
(1) 자동 로드 메커니즘 개요
PHP의 OO 모드를 사용하여 시스템을 개발할 때 일반적으로 각 클래스의 구현을 별도의 파일에 저장하는 것이 일반적입니다. 이렇게 하면 클래스를 쉽게 재사용할 수 있고 향후 유지 관리에도 편리할 것입니다. 이는 OO 디자인의 기본 아이디어 중 하나이기도 합니다. PHP5 이전에는 클래스를 사용해야 하는 경우 include/require를 사용하여 직접 포함하기만 하면 되었습니다. 실제 예는 다음과 같습니다.
코드는 다음과 같습니다.
/* Person.class.php */ <?php class Person { var $name, $age; function construct ($name, $age) { $this->name = $name; $this->age = $age; } } ?> /* no_autoload.php */ <?php require_once (”Person.class.php”); $person = new Person(”Altair”, 6); var_dump ($person); ?>
이 예에서 no-autoload.php 파일은 require_once를 사용하여 포함하는 Person 클래스를 사용해야 하며 직접 사용할 수 있습니다. 인스턴스화하기 위한 Person 클래스는 객체를 변환합니다.
그러나 프로젝트 규모가 계속 확장됨에 따라 이 방법을 사용하면 몇 가지 숨겨진 문제가 발생합니다. PHP 파일이 다른 많은 클래스를 사용해야 하는 경우 많은 require/include 문이 필요하므로 누락이 발생할 수 있습니다. 불필요한 클래스 파일을 포함합니다. 많은 수의 파일에 다른 클래스를 사용해야 하는 경우 각 파일에 올바른 클래스 파일이 포함되어 있는지 확인하는 것은 악몽이 될 것입니다.
PHP5는 클래스의 자동 로딩(autoload) 메커니즘으로 이 문제에 대한 해결책을 제공합니다. 자동 로드 메커니즘을 사용하면 처음부터 모든 클래스 파일을 포함하는 대신 PHP 프로그램이 클래스를 사용할 때만 클래스 파일을 자동으로 포함할 수 있습니다. 이 메커니즘을 지연 로딩이라고도 합니다.
다음은 자동 로드 메커니즘을 사용하여 Person 클래스를 로드하는 예입니다.
코드는 다음과 같습니다.
/* autoload.php */ <?php function autoload($classname) { require_once ($classname . “class.php”); } $person = new Person(”Altair”, 6); var_dump ($person); ?>
보통 PHP5가 클래스를 사용할 때 클래스가 로드되지 않은 것을 발견하면 autoload() 함수를 자동으로 실행합니다. 이 함수에서는 사용해야 하는 클래스를 로드할 수 있습니다. 간단한 예에서는 "title="extension">확장자 ".class.php" 확장자를 사용하여 클래스 이름을 직접 추가하여 클래스 파일 이름을 구성한 다음 require_once를 사용하여 이를 로드합니다. 이 예에서 다음을 수행할 수 있습니다. 자동 로드는 최소한 세 가지 작업을 수행해야 합니다. 첫 번째는 클래스 이름을 기반으로 클래스 파일 이름을 결정하는 것이고, 두 번째는 클래스 파일이 있는 디스크 경로를 결정하는 것입니다(이 예에서는 가장 간단한 경우입니다. 클래스는 이를 호출하는 PHP 프로그램 파일과 동일한 폴더에 있습니다. 세 번째는 디스크 파일에서 시스템으로 클래스를 로드하는 것입니다. 세 번째 단계는 단지 include/require를 사용하는 것입니다. 첫 번째 단계인 두 번째 단계 기능은 개발 중에 클래스 이름과 디스크 파일 간의 매핑 방법에 동의해야 합니다. 이 방법으로만 클래스 이름에 따라 해당 디스크 파일을 찾을 수 있습니다. 포함할 클래스 파일이 많은 경우에는 해당 규칙만 결정한 다음 autoload() 함수에서 클래스 이름을 실제 디스크 파일과 일치시키면 지연 로딩 효과를 얻을 수 있습니다. 여기에서 autoload() 함수의 구현도 볼 수 있습니다. 가장 중요한 것은 클래스 이름과 실제 디스크 파일 간의 매핑 규칙 구현입니다.
그러나 이제 다른 많은 클래스를 사용해야 하는 경우 문제가 발생합니다. 시스템 구현 시 이러한 클래스 라이브러리는 서로 다른 개발자에 의해 개발될 수 있습니다. 이때 자동 로딩을 구현하려는 경우 클래스 이름과 클래스 라이브러리에 의해 작성된 실제 디스크 파일 간의 매핑 규칙이 다릅니다. 클래스 라이브러리 파일의 경우 autoload() 함수에서 모든 매핑 규칙을 구현해야 하므로 autoload() 함수는 구현하기가 매우 복잡하거나 심지어 구현이 불가능할 수도 있습니다. 구현 가능하더라도 향후 유지 관리 및 시스템 효율성에 큰 부정적인 영향을 미칠 것입니다. 이 경우 더 간단하고 명확한 솔루션은 없을까요? 물론 추가 솔루션을 살펴보기 전에, 먼저 PHP의 자동 로드 메커니즘이 어떻게 구현되는지 살펴보겠습니다.
(2) PHP의 자동 로드 메커니즘 구현
PHP 파일의 실행이 두 개의 독립적인 프로세스로 나누어져 있다는 것을 알고 있습니다. PHP 파일을 일반적으로 OPCODE라고 하는 바이트 코드 시퀀스(실제로는 zend_op_array라는 바이트 배열로 컴파일됨)로 컴파일합니다. 두 번째 단계는 가상 머신에서 이러한 OPCODE를 실행하는 것입니다. 따라서 PHP의 모든 동작은 이러한 OPCODE에 의해 구현됩니다. PHP에서 자동 로드의 구현 메커니즘을 연구합니다. autoload.php 파일은 opcode로 컴파일된 다음 이러한 OPCODE를 기반으로 PHP가 프로세스에서 수행한 작업을 연구합니다. /* autoload.php의 컴파일된 OPCODE 목록이 만들어집니다. 저자가 개발한 OPDUMP 도구를 사용하여
*/
코드는 다음과 같습니다.
<?php // require_once (”Person.php”); function autoload ($classname) { 0 NOP 0 RECV 1 if (!class_exists($classname)) { 1 SEND_VAR !0 2 DO_FCALL ‘class_exists' [extval:1] 3 BOOL_NOT $0 =>RES[~1] 4 JMPZ ~1, ->8 require_once ($classname. “.class.php”); 5 CONCAT !0, ‘.class.php' =>RES[~2] 6 INCLUDE_OR_EVAL ~2, REQUIRE_ONCE } 7 JMP ->8 } 8 RETURN null $p = new Person('Fred', 35); 1 FETCH_CLASS ‘Person' =>RES[:0] 2 NEW :0 =>RES[$1] 3 SEND_VAL ‘Fred' 4 SEND_VAL 35 5 DO_FCALL_BY_NAME [extval:2] 6 ASSIGN !0, $1 var_dump ($p); 7 SEND_VAR !0 8 DO_FCALL ‘var_dump' [extval:1] ?>
在 autoload.php的第10行代码中我们需要为类Person实例化一个对象。因此autoload机制一定会在该行编译后的opcode中有所体 现。从上面的第10行代码生成的OPCODE中我们知道,在实例化对象Person时,首先要执行FETCH_CLASS指令。我们就从PHP对 FETCH_CLASS指令的处理过程开始我们的探索之旅。
通过查阅PHP的源代码(我使用的是PHP 5.3alpha2版本)可以发现如下的调用序列:
ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, …) (zend_vm_def.h 1864行)
=> zend_fetch_class (zend_execute_API.c 1434行)
=>zend_lookup_class_ex (zend_execute_API.c 964行)
=> zend_call_function(&fcall_info, &fcall_cache) (zend_execute_API.c 1040行)
在最后一步的调用之前,我们先看一下调用时的关键参数:
/* 设置autoload_function变量值为”autoload” */
fcall_info.function_name = &autoload_function; // Ooops, 终于发现”autoload”了
…
fcall_cache.function_handler = EG(autoload_func); // autoload_func !
zend_call_function 是Zend Engine中最重要的函数之一,其主要功能是执行用户在PHP程序中自定义的函数或者PHP本身的库函数。zend_call_function有两个 重要的指针形参数fcall_info, fcall_cache,它们分别指向两个重要的结构,一个是zend_fcall_info, 另一个是zend_fcall_info_cache。zend_call_function主要工作流程如下:如果 fcall_cache.function_handler指针为NULL,则尝试查找函数名为fcall_info.function_name的函 数,如果存在的话,则执行之;如果fcall_cache.function_handler不为NULL,则直接执行 fcall_cache.function_handler指向的函数。
现在我们清楚了,PHP在实例化一个 对象时(实际上在实现接口,使用类常数或类中的静态变量,调用类中的静态方法时都会如此),首先会在系统中查找该类(或接口)是否存在,如果不存在的话就 尝试使用autoload机制来加载该类。而autoload机制的主要执行过程为:
(1) 检查执行器全局变量函数指针autoload_func是否为NULL。
(2) 如果autoload_func==NULL, 则查找系统中是否定义有autoload()函数,如果没有,则报告错误并退出。
(3) 如果定义了autoload()函数,则执行autoload()尝试加载类,并返回加载结果。
(4) 如果autoload_func不为NULL,则直接执行autoload_func指针指向的函数用来加载类。注意此时并不检查autoload()函数是否定义。
真 相终于大白,PHP提供了两种方法来实现自动装载机制,一种我们前面已经提到过,是使用用户定义的autoload()函数,这通常在PHP源程序中 来实现;另外一种就是设计一个函数,将autoload_func指针指向它,这通常使用C语言在PHP扩展中实现。如果既实现了 autoload()函数,又实现了autoload_func(将autoload_func指向某一PHP函数),那么只执行 autoload_func函数。
(3) SPL autoload机制的实现
SPL 是Standard PHP Library(标准PHP库)的缩写。它是PHP5引入的一个扩展库,其主要功能包括autoload机制的实现及包括各种Iterator接口或类。 SPL autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。SPL有两个不同的函数 spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。
spl_autoload 是SPL实现的默认的自动加载函数,它的功能比较简单。它可以接收两个参数,第一个参数是$class_name,表示类名,第二个参 数$file_extensions是可选的,表示类文件的扩展名" title="扩展名">扩展名,可以在$file_extensions中指定多个扩展名" title="扩展名">扩展名,护展名之间用分号隔开即 可;如果不指定的话,它将使用默认的扩展名" title="扩展名">扩展名.inc或.php。spl_autoload首先将$class_name变为小写,然后在所有的 include path中搜索$class_name.inc或$class_name.php文件(如果不指定$file_extensions参数的话),如果找 到,就加载该类文件。你可以手动使用spl_autoload(”Person”, “.class.php”)来加载Person类。实际上,它跟require/include差不多,不同的它可以指定多个扩展名" title="扩展名">扩展名。
spl_autoload가 자동으로 작동하도록 하는 방법, 즉 autoload_func가 spl_autoload를 가리키도록 하는 방법은 무엇입니까? 대답은 spl_autoload_register 함수를 사용하는 것입니다. 매개 변수 없이 PHP 스크립트에서 처음으로 spl_autoload_register()를 호출하면 autoload_func에서 spl_autoload를 지정할 수 있습니다.
위의 설명을 통해 우리는 spl_autoload의 기능이 상대적으로 간단하고 SPL 확장에 구현되어 있으며 해당 기능을 확장할 수 없다는 것을 알고 있습니다. 보다 유연한 자동 로딩 메커니즘을 구현하고 싶다면 어떻게 해야 할까요? 이때 spl_autoload_call 기능이 데뷔합니다.
먼저 spl_autoload_call 구현의 놀라운 기능을 살펴보겠습니다. SPL 모듈 내부에는 기본적으로 HashTable인 전역 변수 autoload_functions가 있지만 간단히 연결 목록으로 생각할 수 있습니다. 연결 목록의 각 요소는 autoloading 클래스 함수 함수를 가리키는 함수 포인터입니다. . spl_autoload_call 구현 자체는 매우 간단하며, 단순히 연결 리스트에 있는 각 함수를 순서대로 실행한 후, 필요한 클래스가 로드되었는지 판단하여 로딩에 성공하면 바로 반환을 하게 됩니다. 연결된 목록의 다른 기능을 계속 실행합니다. 이 연결된 목록의 모든 함수가 실행된 후에도 클래스가 로드되지 않은 경우 spl_autoload_call은 사용자에게 오류를 보고하지 않고 직접 종료됩니다. 따라서 자동 로드 메커니즘을 사용한다고 해서 클래스가 자동으로 올바르게 로드되는 것은 아닙니다. 핵심은 여전히 자동 로드 기능이 구현되는 방식에 따라 달라집니다.
그렇다면 자동 로딩 기능 목록 autoload_functions은 누가 관리하게 될까요? 앞서 언급한 spl_autoload_register 함수입니다. 이 연결 목록에 사용자 정의 자동 로딩 함수를 등록하고 autoload_func 함수 포인터를 spl_autoload_call 함수에 지정할 수 있습니다(예외가 있으며 특정 상황은 모두가 생각하도록 남겨둡니다). spl_autoload_unregister 함수를 통해 autoload_functions 연결 목록에서 등록된 함수를 삭제할 수도 있습니다.
이전 섹션에서 언급했듯이 autoload_func 포인터가 null이 아니면 autoload() 함수가 자동으로 실행되지 않습니다. 이제 autoload_func가 spl_autoload_call을 가리켰습니다. 일하다? 물론, 여전히 spl_autoload_register(autoload) 호출을 사용하여 autoload_functions 연결 목록에 등록합니다.
이제 첫 번째 섹션의 마지막 질문으로 돌아가서 해결책이 있습니다. 각 클래스 라이브러리의 다양한 명명 메커니즘에 따라 자체 자동 로딩 기능을 구현한 다음 spl_autoload_register를 사용하여 SPL 자동 로딩 기능에 등록합니다. 대기줄. . 이렇게 하면 매우 복잡한 자동 로드 기능을 유지할 필요가 없습니다.
(4) 자동 로드 효율성 문제 및 대책
자동 로드 메커니즘을 사용할 때 많은 사람들의 첫 번째 반응은 자동 로드를 사용하면 시스템 효율성이 떨어진다는 것입니다. 어떤 사람들은 효율성을 위해 자동 로드를 사용하지 말라고 제안하기도 합니다. 자동 로드 구현의 원리를 이해한 후에는 자동 로드 메커니즘 자체가 시스템 효율성에 영향을 미치는 이유가 아니라는 것을 알게 됩니다. 자동 로드는 불필요한 클래스를 시스템에 로드하지 않기 때문에 시스템 효율성을 향상시킬 수도 있습니다.
그렇다면 왜 많은 사람들은 자동 로드를 사용하면 시스템 효율성이 떨어진다고 생각할까요? 실제로 자동 로드 메커니즘의 효율성에 영향을 미치는 것은 바로 사용자가 설계한 자동 로드 기능입니다. 클래스 이름을 실제 디스크 파일과 효율적으로 일치시킬 수 없는 경우(참고: 이는 파일 이름뿐만 아니라 실제 디스크 파일을 나타냄) 시스템은 많은 파일 존재 확인을 수행해야 합니다(각 포함 경로에 )에 포함된 경로를 검색하고 파일이 있는지 확인하려면 디스크 I/O 작업이 필요합니다. 우리 모두 알고 있듯이 디스크 I/O 작업의 효율성이 매우 낮기 때문에 이것이 자동 로드의 효율성을 감소시키는 원인입니다. 메커니즘!
따라서 시스템을 설계할 때 클래스 이름을 실제 디스크 파일에 매핑하는 명확한 메커니즘을 정의해야 합니다. 이 규칙이 더 간단하고 명확할수록 자동 로드 메커니즘은 더 효율적입니다.
결론: 자동 로드 메커니즘은 본질적으로 비효율적입니다. 자동 로드의 남용과 잘못 설계된 자동 로드 기능만이 효율성 감소로 이어질 것입니다.
위 내용은 자동 로드와 spl_autoload 자동 로드 비교 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!