Let’s say you have the file Rectangle.php which contains the definition for a Rectangle class. Before you can create an instance of the object elsewhere in your code, you first need to pull in the Rectangle.php file, perhaps by writing something like this:
假设您有文件Rectangle.php,其中包含Rectangle类的定义。 在可以在代码中的其他位置创建对象的实例之前,首先需要拉入Rectangle.php文件,也许可以通过编写如下代码:
<?php require "Rectangle.php"; $rect = new Rectangle(42, 25);Normally we put each class’ definition in its own file for better organization, and so you need to require/include each of the class files you want to use. If there are only a few files then it isn’t too much of a problem, but oftentimes that’s not the case. It can be very cumbersome to load a large library including all of its dependencies like this.
通常,我们将每个类的定义放在自己的文件中,以便更好地组织,因此您需要要求/包括要使用的每个类文件。 如果只有几个文件,那么问题就不多了,但是通常情况并非如此。 加载包含所有此类依赖项的大型库可能非常麻烦。
In this article I’ll walk you through the “history of autoloading,” from the older to the current PSR-0 standard autoloader approach found in many PHP frameworks such as Lithium, Symfony, Zend, etc. Then I will introduce you to the ClassLoader component from the Symfony2 project for PHP 5.3 which follows the PSR-0 standard.
在本文中,我将向您介绍“自动加载的历史”,从旧的到现在在许多PHP框架(如Lithium,Symfony,Zend等)中发现的PSR-0标准自动加载器方法。然后,我将向您介绍来自Symfony2项目PHP 5.3的 ClassLoader组件,该组件遵循PSR-0标准 。
Warning: Most of the code samples in the beginning of this article demonstrate deprecated approaches. It would be unwise to use them in production. I recommend you use one of the PSR-0 standard autoloaders instead.
警告:本文开头的大多数代码示例都演示了不建议使用的方法。 在生产中使用它们是不明智的。 我建议您改为使用PSR-0标准自动装带器之一。
PHP 5 introduced the magic function __autoload() which is automatically called when your code references a class or interface that hasn’t been loaded yet. This provides the runtime one last chance to load the definition before PHP fails with an error.
PHP 5引入了魔术函数__autoload() ,当您的代码引用尚未加载的类或接口时,该函数会自动调用。 这为运行时提供了在PHP因错误而失败之前加载定义的最后机会。
Here’s an example of an extremely basic __autoload() implementation:
这是一个非常基本的__autoload()实现的示例:
<?php function __autoload($className) { $filename = $className . ".php"; if (is_readable($filename)) { require $filename; } }It’s a good idea to make sure a file exists before you try to include it, but sometimes the file may be there but will not have sufficient read permissions so it’s better to use is_readable() over file_exists() which will for test both conditions.
最好在包含文件之前先确保文件存在,但有时文件可能存在但没有足够的读取权限,因此最好使用is_readable()不是file_exists()来测试这两种情况。
The major drawback to the __autoload() function is that you can only provide one autoloader with it. PHP 5.1.2 introduced spl_autoload() which allows you to register multiple autoloader functions, and in the future the __autoload() function will be deprecated.
__autoload()函数的主要缺点是只能提供一个自动装带器。 PHP 5.1.2引入了spl_autoload() ,它允许您注册多个自动加载器功能,并且将来不推荐使用__autoload()函数。
The introduction of spl_autoload_register() gave programmers the ability to create an autoload chain, a series of functions that can be called to try and load a class or interface. For example:
spl_autoload_register()的引入使程序员能够创建自动加载链,自动加载链是可以调用以尝试加载类或接口的一系列功能。 例如:
<?php function autoloadModel($className) { $filename = "models/" . $className . ".php"; if (is_readable($filename)) { require $filename; } } function autoloadController($className) { $filename = "controllers/" . $className . ".php"; if (is_readable($filename)) { require $filename; } } spl_autoload_register("autoloadModel"); spl_autoload_register("autoloadController");Generally the functions are called in the order they’re registered, but the order can also be affected by additional arguments passed to spl_autoload_register().
通常,函数以注册顺序调用,但是传递给spl_autoload_register()附加参数也可能影响顺序。
It’s important to remember that once a function has been registered with spl_autoload_register(), the __autoload() function will no longer be called. If you have an __autoload() function you want to run as part of your autoloader chain, then you’ll have to register it with spl_autoload_register().
重要的是要记住,一旦在spl_autoload_register()注册了函数,就将不再调用__autoload()函数。 如果您要使用__autoload()函数作为自动加载器链的一部分运行,则必须使用spl_autoload_register()进行注册。
Of course, the implementations of the autoloading functions I’ve shown this far have been rather simple. Real-world autoloaders are more complex.
当然,到目前为止,我已经展示了自动加载功能的实现非常简单。 实际的自动装带器更加复杂。
Before real namespace support was introduced in PHP 5.3, developers devised their own approaches to prevent naming collisions. The PEAR Coding Standard used underscores to prefix class names with their directory path; the class Zend_Translate for example would be defined in the file Zend/Translate.php. The autoloader needed to replace the underscores with directory separators to locate the definition.
在PHP 5.3中引入真正的名称空间支持之前,开发人员已经设计了自己的方法来防止命名冲突。 PEAR编码标准使用下划线在类名称的目录路径前添加前缀; 例如,类Zend_Translate将在文件Zend/Translate.php定义。 自动加载器需要用目录分隔符替换下划线以找到定义。
Also, different developers adopted different conventions when it came to naming their class files, for example the files might end in .php, .class.php, .inc, etc. Some libraries may be installed in different paths as well. The loader needed to look in various places for them, so now the loader begins to look like this:
同样,不同的开发人员在命名其类文件时采用了不同的约定,例如,文件可能以.php , .class.php , .inc等结尾。某些库也可能安装在不同的路径中。 加载程序需要在各个地方寻找它们,因此现在加载程序开始看起来像这样:
<?php function __autoload($className) { $extensions = array(".php", ".class.php", ".inc"); $paths = explode(PATH_SEPARATOR, get_include_path()); $className = str_replace("_" , DIRECTORY_SEPARATOR, $className); foreach ($paths as $path) { $filename = $path . DIRECTORY_SEPARATOR . $className; foreach ($extensions as $ext) { if (is_readable($filename . $ext)) { require_once $filename . $ext; break; } } } }Autoloading is a useful idea, but was an idea that desperately needed some standardization.
自动加载是一个有用的想法,但是这个想法非常需要一些标准化。
After PHP 5.3’s introduction of true namespace support, a group of people from the PHP community decided to create the PHP Standards Working Group in 2009 (later renamed to the Framework Interoperatability Group) and establish the PSR-0 standard which outlines various practices and constraints that must be followed for autoloader interoperability. Below are the requirements for PSR-0 compliance:
在PHP 5.3引入了真正的名称空间支持之后,来自PHP社区的一群人决定在2009年创建PHP标准工作组(后更名为Framework Interoperatability Group ),并建立PSR-0标准,该标准概述了各种实践和自动装带器互操作性必须遵循的约束。 以下是符合PSR-0的要求:
A fully-qualified namespace and class must have the following structure <Vendor Name>(<Namespace>)*<Class Name>.
完全限定的名称空间和类必须具有以下结构<Vendor Name>(<Namespace>)*<Class Name> 。
Each namespace must have a top-level namespace (“Vendor Name”). 每个名称空间都必须具有顶级名称空间(“供应商名称”)。 Each namespace can have as many sub-namespaces as it wishes. 每个名称空间可以根据需要具有任意数量的子名称空间。 Each namespace separator is converted to a DIRECTORY_SEPARATOR when loading from the file system. 从文件系统加载时,每个名称空间分隔符都会转换为DIRECTORY_SEPARATOR。Each underscore in the class name is converted to a DIRECTORY_SEPARATOR. The underscore has no special meaning in the namespace.
类名称中的每个下划线都将转换为DIRECTORY_SEPARATOR 。 下划线在名称空间中没有特殊含义。
The fully-qualified namespace and class is suffixed with .php when loading from the file system.
从文件系统加载时,标准的名称空间和类的后缀为.php 。
Alphabetic characters in vendor names, namespaces, and class names may be of any combination of lower case and upper case. 供应商名称,名称空间和类名称中的字母字符可以是小写和大写的任意组合。According to the PSR-0 standard, there should be a top level directory with the vendor’s name and then the package name, so the directory tree will look like this:
根据PSR-0标准,应该有一个顶层目录,其中包含供应商名称和软件包名称,因此目录树将如下所示:
The classes would then be namespaced accordingly:
然后将相应地命名类的名称空间:
<?php namespace VendorPackage; class Example { }Thus, the class definition for DoctrineCommonConnections would be found at /path/to/project/lib/Doctrine/Common/Connections.php, and SymfonyCoreRequest at /path/to/project/lib/Symfony/Core/Request.php. The PSR-0 standard does not mandate what the base /path/to/project/lib portion of the path is, and conforming autoloaders offer different methods for its resolution. Some will allow you to register the directory, some will search PHP’s include_path, and some offer you both. Below is an example taken from the accepted PSR-0 standard.
因此,类定义DoctrineCommonConnections将在这里找到/path/to/project/lib/Doctrine/Common/Connections.php ,并SymfonyCoreRequest在/path/to/project/lib/Symfony/Core/Request.php 。 PSR-0标准不要求/path/to/project/lib的基本/path/to/project/lib部分是什么,并且符合标准的自动加载器为其解析提供了不同的方法。 有些可以让您注册目录,有些可以搜索PHP的include_path ,有些可以同时提供两者。 以下是取自公认的PSR-0标准的示例。
<?php function autoload($className) { $className = ltrim($className, '\'); $fileName = ''; $namespace = ''; if ($lastNsPos = strripos($className, '\')) { $namespace = substr($className, 0, $lastNsPos); $className = substr($className, $lastNsPos + 1); $fileName = str_replace('\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; } $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; require $fileName; }This gist by Jonathan Wage is a sample SplClassLoader implementation that can load your classes if you follow the autoloader interoperability standards. It is the current recommended way to load PHP 5.3 classes that follow these standards.
Jonathan Wage的要点是SplClassLoader示例实现,如果您遵循自动加载器互操作性标准,则可以加载您的类。 这是当前建议的加载遵循这些标准PHP 5.3类的方法。
You can use any one of the PSR-0 compliant autoloaders from frameworks such as Symfony, Pear2, AuraPHP (which is for PHP 5.4+), etc. and adhere to the rules above with your own code to take advantage of autoloading without the uncertainties I discussed previously.
您可以使用Symfony,Pear2,AuraPHP(适用于PHP 5.4+)等框架中的任何一种符合PSR-0的自动加载器,并使用自己的代码遵守上述规则,以利用自动加载的便利性我之前讨论过。
The Symfony2 project is a component-based framework for PHP 5.3 and greater which you can use as a component library or as a full-stack framework. You can download Symfony’s Autoloader, the ClassLoader component, via different means — pear.symfony.com, packagist, or from GitHub.
Symfony2项目是PHP 5.3及更高版本的基于组件的框架,您可以将其用作组件库或全栈框架。 您可以通过不同的方式( pear.symfony.com , packagist )或从GitHub下载Symfony的Autoloader(ClassLoader组件)。
Here’s the directory structure of Symfony’s ClassLoader component:
这是Symfony的ClassLoader组件的目录结构:
Using the component then looks like this:
然后使用该组件如下所示:
<?php require_once "/path/to/Symfony/Component/ClassLoader/UniversalClassLoader.php"; use Symfony\Component\ClassLoader\UniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerNamespace("Symfony\Component" => "/path/to/symfony/components"); $loader->registerNamespace("Monolog" => "path/to/monolog/src/"); $loader->registerPrefix("Zend_", "path/to/zend/library"); $loader->register();The registerNamespace() method is used to inform the autoloader where the given namespace’s base directory maps to on the file system and accepts a namespace as its first argument and path as its second value. You can also register multiple namespaces in a single call with the registerNamespaces() method.
registerNamespace()方法用于通知自动加载器给定名称空间的基本目录在文件系统上的映射位置,并接受名称空间作为其第一个参数,并接受路径作为其第二个值。 您还可以使用registerNamespaces()方法在一个调用中注册多个名称空间。
<?php $loader->registerNamespaces(array( "Symfony\Component" => "/path/to/symfony/components", "Monolog' => "path/to/monolog/src"));The registerPrefix() method is used to register pseudo-namespaces which was used by Pear, Zend, and other libraries and frameworks before real namespace support was implemented in PHP as we have already covered above. You can also register mulitple ones with the registerPrefixes() method and passing it as an associative array.
如上文所述,在PHP中实现真正的名称空间支持之前, registerPrefix()方法用于注册Pear,Zend和其他库和框架使用的伪名称空间。 您还可以使用registerPrefixes()方法registerPrefixes() ,并将其作为关联数组传递。
<?php $loader->registerPrefixes(array( "Zend_" => "/path/to/zend/library", "Twig_" => "path/to/twig/library"));If you are using the Alternative PHP Cache (APC), a free and open source opcode cache for PHP, then you can may want to consider using the ApcUniversalClassLoader class. The ApcUniversalClassLoader extends the UniversalClassLoader but uses apc_store() and apc_fetch() to store lookup information in APC’s cache. The standard UniversalClassLoader will of course work with APC, but the additional behavior offered by the ApcUniversalClassLoader class afford extra performance benefit.
如果您使用的是备用PHP缓存(APC),这是PHP的免费开放源代码操作码缓存,则可能需要考虑使用ApcUniversalClassLoader类。 ApcUniversalClassLoader扩展了UniversalClassLoader但使用apc_store()和apc_fetch()将查找信息存储在APC的缓存中。 标准的UniversalClassLoader当然可以与APC一起使用,但是ApcUniversalClassLoader类提供的其他行为可带来额外的性能优势。
<?php require_once "path/to/Symfony/Component/ClassLoader/UniversalClassLoader.php"; require_once "path/to/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php"; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; $loader = new ApcUniversalClassLoader("apc.prefix.");The ApcUniversalClassLoader accepts a prefix with its constructor. For more information on APC, I suggest reading the APC documentation.
ApcUniversalClassLoader接受带有其构造函数的前缀。 有关APC的更多信息,建议阅读APC文档 。
In this article we have discussed autoloading, from its early days to the current PSR-0 standard which has become widely adopted across many PHP frameworks. Recently, David Coallier tried to push the SplClassloader class into PHP 5.4 to offer native PSR-0 compliant autoloading functionality, but for various reasons it didn’t happen. Maybe in the future we will see it added. (See the C extension at gist.github.com/1310352.)
在本文中,我们讨论了自动加载,从早期到当前的PSR-0标准,该标准已在许多PHP框架中广泛采用。 最近,David Coallier尝试将SplClassloader入PHP 5.4中,以提供与PSR-0兼容的本机自动加载功能,但是由于种种原因却没有实现。 也许将来我们会看到它的添加。 (请参阅gist.github.com/1310352的C扩展名。)
Now the current hot discussion in the working group is focused on caching. If it’s something you’d like to be part of, feel free to join the discussion!
现在, 工作组中当前的热门讨论集中在缓存上。 如果您想成为其中的一员,请随时加入讨论!
Image via Matyas Szabo / Shutterstock
图片来自Matyas Szabo / Shutterstock
And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Jump Start PHP.
并且,如果您喜欢阅读这篇文章,您会喜欢Learnable的 ; 向大师学习新鲜技能的地方。 会员可以立即访问所有SitePoint的电子书和交互式在线课程,例如Jump Start PHP 。
Comments on this article are closed. Have a question about PHP? Why not ask it on our forums?
本文的评论已关闭。 对PHP有疑问吗? 为什么不在我们的论坛上提问呢?
翻译自: https://www.sitepoint.com/autoloading-and-the-psr-0-standard/