php中autoload的实现例子介绍

autoload在php中其实是一个魔术方法了,我们可以指定类目录及规则可以自动加载类文件从而可以省去我们使用include来加载文件了,下面一直来看看关于autoload方法一些例子.

我们在写web应用程序时通常对每个类都建立一个 PHP 源文件,为了使用这些源文件,我们就需要在每个脚本开头写大量的的包含语句(include,require),在 PHP 5 中,不再需要这样了,我们可__autoload()函数和spl_autoload_register函数实现实现自己的加载源文件的机制,它们会在试图使用尚未被定义的类时自动调用.

通过调用这些函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类,本文的主要目标是讲述如何在扩展中用C语言实现自动加载源文件的机制,但是在这之前我们先熟悉一下在PHP脚本中实现自动加载的方法.

在脚本中实现自动加载

在 PHP 5 中我们可以定义一个 __autoload() 函数,它会在试图使用尚未被定义的类时自动调用,这样我们就可以定义一些自己的加载规则了.

  1. <?php
  2. function __autoload($class_name) {
  3. require_once $class_name . '.php';
  4. }
  5. $obj = new MyClass1();
  6. $obj2 = new MyClass2();
  7. ?>

使用spl_autoload_register我们可以一次注册多个加载函数,PHP会在试图使用尚未被定义的类时按注册顺序调用.

  1. <?php
  2. function autoload_services($class_name)
  3. {
  4. $file = 'services/' . $class_name. '.php';
  5. if (file_exists($file))
  6. {
  7. require_once($file);
  8. }
  9. }
  10. function autoload_vos($class_name)
  11. {
  12. $file = 'vos/' . $class_name. '.php';
  13. if (file_exists($file))
  14. {
  15. require_once($file);
  16. }
  17. }
  18. spl_autoload_register('autoload_services');
  19. spl_autoload_register('autoload_vos');
  20. ?>

在php扩展中实现自动加载

最近在写一个php扩展,其中一个功能就是实现类的自动加载,其实也是通过在内核中调用spl_autoload_register函数来实现,使用zend API调用spl_autoload_register函数还是相对简单的,下面我们主要讲一下如何在内核中实现inclue/require/include_once/require_once等指令的功能,其实inclue/require/include_once/require_once等指令主要是读入文件编译并执行,下面的方法就是完成了这些操作,代码中有详细的注释.

  1. /*
  2. * loader_import首先将PHP源文件编译成op_array,然后依次执行op_array中的opcode
  3. */
  4. int loader_import(char *path, int len TSRMLS_DC) {
  5. zend_file_handle file_handle;
  6. zend_op_array *op_array;
  7. char realpath[MAXPATHLEN];
  8. if (!VCWD_REALPATH(path, realpath)) {
  9. return 0;
  10. }
  11. file_handle.filename = path;
  12. file_handle.free_filename = 0;
  13. file_handle.type = ZEND_HANDLE_FILENAME;
  14. file_handle.opened_path = NULL;
  15. file_handle.handle.fp = NULL;
  16. //调用zend API编译源文件
  17. op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
  18. if (op_array && file_handle.handle.stream.handle) {
  19. int dummy = 1;
  20. if (!file_handle.opened_path) {
  21. file_handle.opened_path = path;
  22. }
  23. //将源文件注册到执行期间的全局变量(EG)的include_files列表中,这样就标记了源文件已经包含过了
  24. zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy,
  25. sizeof(int), NULL);
  26. }
  27. zend_destroy_file_handle(&file_handle TSRMLS_CC);
  28. //开始执行op_array
  29. if (op_array) {
  30. zval *result = NULL;
  31. //保存原来的执行环境,包括active_op_array,opline_ptr等
  32. zval ** __old_return_value_pp = EG(return_value_ptr_ptr);
  33. zend_op ** __old_opline_ptr = EG(opline_ptr);
  34. zend_op_array * __old_op_array = EG(active_op_array);
  35. //保存环境完成后,初始化本次执行环境,替换op_array
  36. EG(return_value_ptr_ptr) = &result;
  37. EG(active_op_array) = op_array;
  38. #if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
  39. if (!EG(active_symbol_table)) {
  40. zend_rebuild_symbol_table(TSRMLS_C);
  41. }
  42. #endif
  43. //调用zend API执行源文件的op_array
  44. zend_execute(op_array TSRMLS_CC);
  45. //op_array执行完成后销毁,要不然就要内存泄露了,哈哈
  46. destroy_op_array(op_array TSRMLS_CC);
  47. efree(op_array);
  48. //通过检查执行期间的全局变量(EG)的exception是否被标记来确定是否有异常
  49. if (!EG(exception)) {
  50. if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) {
  51. zval_ptr_dtor(EG(return_value_ptr_ptr));
  52. } //开源软件:phpfensi.com
  53. }
  54. //ok,执行到这里说明源文件的op_array已经执行完成了,我们要恢复原来的执行环境了
  55. EG(return_value_ptr_ptr) = __old_return_value_pp;
  56. EG(opline_ptr) = __old_opline_ptr;
  57. EG(active_op_array) = __old_op_array;
  58. return 1;
  59. }
  60. return 0;
  61. }