如何在PHP中处理所需的类
我想知道最佳解决方案是什么,因为必须在PHP脚本中"包含"如此多的文件以确保我的脚本可以访问所有需要使用的类,从而解决该问题。
目前,我只是使用include_once来包含我直接访问的类。每一个都将" include_once"他们访问的类。
我已经研究过使用__autoload函数,但是如果我们打算将类文件组织在目录树中,那么hat似乎不能很好地工作。如果这样做,似乎我们最终将遍历目录树,直到找到所需的类。另外,我不确定这如何影响不同名称空间中具有相同名称的类。
有没有更简单的方法来解决这个问题?
还是PHP只是不适合具有许多不同对象的"企业"类型应用程序,这些对象都位于可以位于许多不同目录的单独文件中。
解决方案
回答
如果类具有一致的命名约定,可以告诉函数在目录树中的位置,则__autoload会很好地工作。 MVC非常适合此类事情,因为我们可以轻松地将类拆分为模型,视图和控制器。
另外,为类的文件位置保留一个名称关联数组,然后让__autoload查询该数组。
回答
__autoload
将起作用,但仅在PHP 5中起作用。
回答
我们可以使用spl_autoload_register定义多个自动加载功能:
spl_autoload_register('load_controllers'); spl_autoload_register('load_models'); function load_models($class){ if( !file_exists("models/$class.php") ) return false; include "models/$class.php"; return true; } function load_controllers($class){ if( !file_exists("controllers/$class.php") ) return false; include "controllers/$class.php"; return true; }
回答
到目前为止,在我的建议中,我只支持Kevin的建议,但并不一定要绝对。我看到了__autoload可以使用几个不同的选项。
- 将所有类文件放在一个目录中。用类命名文件,即" classes / User.php"或者" classes / User.class.php"。
- 凯文(Kevin)的想法是将模型放入一个目录,将控制器放入另一个目录,等等。如果我们所有的类都很好地适合MVC框架,则效果很好,但有时情况会变得混乱。
- 在类名中包括目录。例如,一个名为Model_User的类实际上位于
classes / Model / User.php
中。__autoload函数会知道将下划线转换为目录分隔符以查找文件。 - 只需解析整个目录结构一次。或者在__autoload函数中,或者就在定义它的同一PHP文件中,循环遍历
classes
目录的内容并缓存哪些文件在哪里。因此,如果我们尝试加载User
类,则它是否位于classes / User.php
或者classes / Models / User.php
或者classes / Utility / User.php
中都没关系。一旦它在classes
目录中的某个地方找到User.php
,它将在需要自动加载User
类时知道要包含的文件。
回答
@凯文:
I was just trying to point out that spl_autoload_register is a better alternative to __autoload since you can define multiple loaders, and they won't conflict with each other. Handy if you have to include libraries that define an __autoload function as well.
你确定吗?该文档有不同的说法:
If your code has an existing __autoload function then this function must be explicitly registered on the __autoload stack. This is because spl_autoload_register() will effectively replace the engine cache for the __autoload function by either spl_autoload() or spl_autoload_call().
=>我们还必须显式注册任何库的__autoload。但是除了我们当然是对的,此功能是更好的选择。
回答
在我的应用程序中,我通常有一个setup.php
文件,其中包含所有核心类(即框架和随附的库)。我的自定义类是在目录布局图的帮助下使用自动加载器加载的。
每次添加新类时,我都会运行命令行构建器脚本,该脚本会扫描整个目录树以搜索模型类,然后使用类名称作为键和路径作为值来构建关联数组。然后,__ autoload函数在该数组中查找类名称并获取包含路径。这是代码:
autobuild.php
define('MAP', 'var/cache/autoload.map'); error_reporting(E_ALL); require 'setup.php'; print(buildAutoloaderMap() . " classes mapped\n"); function buildAutoloaderMap() { $dirs = array('lib', 'view', 'model'); $cache = array(); $n = 0; foreach ($dirs as $dir) { foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) { $fn = $entry->getFilename(); if (!preg_match('/\.class\.php$/', $fn)) continue; $c = str_replace('.class.php', '', $fn); if (!class_exists($c)) { $cache[$c] = ($pn = $entry->getPathname()); ++$n; } } } ksort($cache); file_put_contents(MAP, serialize($cache)); return $n; }
autoload.php
define('MAP', 'var/cache/autoload.map'); function __autoload($className) { static $map; $map or ($map = unserialize(file_get_contents(MAP))); $fn = array_key_exists($className, $map) ? $map[$className] : null; if ($fn and file_exists($fn)) { include $fn; unset($map[$className]); } }
请注意,文件命名约定必须为[class_name] .class.php。更改目录类后,将在autobuild.php
中查找。当找不到类时,我们也可以从autoload函数运行autobuilder,但这可能会使程序陷入无限循环。
序列化数组的速度很快。
@JasonMichael:PHP 4已死。克服它。
回答
我们还可以通过使用映射到物理目录的结构化命名约定以编程方式确定类文件的位置。这就是Zend在Zend Framework中的工作方式。因此,当我们调用Zend_Loader :: loadClass(" Zend_Db_Table");
时,通过在下划线上进行拆分,它将类名分解为目录数组,然后Zend_Loader类将加载所需的文件。
像所有Zend模块一样,我希望我们可以将自己的加载程序仅与自己的类一起使用,但是我只是将其用作使用Zend MVC的站点的一部分。
但是,当我们使用任何类型的动态类加载时,都会有人担心加载时的性能,例如,请参阅此博客文章,将Zend_Loader与类文件的硬加载进行了比较。
除了必须搜索PHP包含路径而导致的性能损失外,它还会破坏操作码缓存。从对该帖子的评论:
When using ANY Dynamic class loader APC can’t cache those files fully as its not sure which files will load on any single request. By hard loading the files APC can cache them in full.