使用php中的autoload并搜索类文件会降低性能吗?
我一直在努力如何最好地将类包含到我的php代码中。路径通常是一个问题,但几分钟前,我发现了这个问题,这对我们有很大帮助。现在,我正在阅读有关__autoload的内容,并认为它可以使开发应用程序的过程变得更加容易。问题是我喜欢维护文件夹结构以分隔功能区域,而不是将所有内容都放入常规的/ lib文件夹中。因此,如果我覆盖自动加载功能以对包括所有子文件夹的类文件夹进行深入搜索,我可以期望获得哪些性能提升?
显然,这将取决于规模,文件夹结构的深度和类的数量,但通常我在中等规模的项目中会问是否会引起问题。
解决方案
__autoload很棒,但是在递归搜索功能中声明所有文件的成本很高。我们可能需要查看构建用于自动加载的文件树。在我的框架中,我始终为文件的类命名,并使用为数据缓存的映射。
从第68行开始查看http://trac.framewerk.org/cgi-bin/trac.fcgi/browser/trunk/index.php [死链接],以了解如何完成此操作。
编辑:为了更直接地回答问题,而无需进行缓存,我们可以期望在中等流量或者大量流量的站点上出现性能下降的情况。
一种常见的模式(例如,Pear,Zend Framework等)是使类名反映路径,因此Db_Adapter_Mysql将位于/Db/Adapter/Mysql.php中,位于添加到包含路径中的某个位置。
到处搜寻文件会使速度变慢(许多磁盘命中次数)。如果我们可能需要加载所有类,则它们将占用更多内存。在每个文件中指定所需的类很难维护(即,如果不再使用它们,则不会删除它们)。
真正的问题是,哪一个对我们更重要?最终,它们都是权衡的,所以我们必须选择一个。但是,可以争论的是,第二个和第三个选项中的大部分开销都与实际编译代码有关。使用APC之类的东西可以显着减少每次页面加载时加载和编译每个类的开销。
在使用APC的情况下,我可能会采取将代码分成模块的方法(例如,Web界面模块,数据库交互模块等),并让每个模块导入其模块的所有类以及类从他们可能需要的其他模块。这是最后两个之间的权衡,我发现它可以很好地满足我的需求。
我们可以通过2种方法轻松地执行此操作,首先,为类命名,以便它们定义在哪里可以找到它们的结构
function __autoload($classname) { try { if (class_exists($classname, false) OR interface_exists($classname, false)) { return; } $class = split('_', strtolower(strval($classname))); if (array_shift($class) != 'majyk') { throw new Exception('Autoloader tried to load a class that does not belong to us ( ' . $classname . ' )'); } switch (count($class)) { case 1: // Core Class - matches Majyk_Foo - include /core/class_foo.php $file = MAJYK_DIR . 'core/class_' . $class[0] . '.php'; break; case 2: // Subclass - matches Majyk_Foo_Bar - includes /foo/class_bar.php $file = MAJYK_DIR . $class[0] . '/class_' . $class[1] . '.php'; break; default: throw new Exception('Unknown Class Name ( ' . $classname .' )'); return false; } if (file_exists($file)) { require_once($file); if (!class_exists($classname, false) AND !interface_exists($classname, false)) { throw new Exception('Class cannot be found ( ' . $classname . ' )'); } } else { throw new Exception('Class File Cannot be found ( ' . str_replace(MAJYK_DIR, '', $file) . ' )'); } } catch (Exception $e) { // spl_autoload($classname); echo $e->getMessage(); } }
或者,2,使用多个自动加载器。 PHP> = 5.1.2具有SPL库,该库允许我们添加多个自动加载器。我们为每条路径添加一个,它将在通过过程中找到它。或者只是将它们添加到包含路径并使用默认的spl_autoload()
一个例子
function autoload_foo($classname) { require_once('foo/' . $classname . '.php'); } function autoload_bar($classname) { require_once('bar/' . $classname . '.php'); } spl_autoload_register('autoload_foo'); spl_autoload_register('autoload_bar'); spl_autoload_register('spl_autoload'); // Default SPL Autoloader
自动加载是一项很棒的PHP功能,可以极大地...
如果使用类似以下的智能分类法,性能将不会受到影响:
1.每个库都位于"包"文件夹中
2.通过将类名中的" _"替换为" /"并在末尾添加" .php"来定位每个类
类= My_App_Smart_Object
文件=包/My/App/Smart/Object.php
这种方法的好处(几乎所有框架都使用了它)也使代码更聪明:-)
我倾向于使用一种简单的方法,其中__autoload()将哈希映射类名称查询到相对路径,该文件包含在使用一个本身执行递归搜索的简单脚本重新生成的文件中。
这要求在添加新的类文件或者重组代码库时运行脚本,但同时也避免了__autoload()中的"聪明"现象,因为它可能导致不必要的stat()调用,并且它的优点是我可以轻松移动我的代码库中的文件,知道我需要做的就是运行一个脚本来更新自动加载器。
该脚本本身以递归方式检查我的include /目录,并假定未在简短排除列表中命名的任何PHP文件(自动加载器本身,以及我倾向于使用的其他一些标准文件)都包含相同名称的类。
Zend Framework的方法是根据PEAR文件夹标准(Class_Foo映射到/Class/Foo.php)进行自动加载,但是它使用include_path而不是使用设置的基本路径。
他们的方法的问题是无法预先检查文件是否存在,因此自动加载将尝试包含在任何include_path中都不存在的文件,错误提示,并且永远不会提供任何其他通过spl_autoload_register注册的自动加载功能有机会包含文件。
因此,略有偏差是手动提供基本路径数组,自动加载可以期望这些基本路径以PEAR方式查找类设置,并仅循环基本路径:
<?php //... foreach( $paths as $path ) { if( file_exists($path . $classNameToFilePath) ) include $path . $classNameToFilePath; } //... ?>
当然,我们会被搜索到,但是对于每个自动加载,我们只会在最坏的n次搜索中执行操作,其中n是我们要检查的基本路径的数量。
但是,如果我们仍然需要递归扫描目录,那么问题不是"自动加载会损害我的性能吗",那么问题应该是:"为什么我要以随机结构来扔我的类文件?"坚持使用PEAR结构将为我们省去很多麻烦,即使我们决定手动执行包含操作而不是自动加载操作,也不会猜测在执行包含语句时类文件的位置。