242 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
		
		
			
		
	
	
			242 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| 
								 | 
							
								Class Loading
							 | 
						||
| 
								 | 
							
								=============
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Class loading is an essential part of any PHP application that
							 | 
						||
| 
								 | 
							
								makes heavy use of classes and interfaces. Unfortunately, a lot of
							 | 
						||
| 
								 | 
							
								people and projects spend a lot of time and effort on custom and
							 | 
						||
| 
								 | 
							
								specialized class loading strategies. It can quickly become a pain
							 | 
						||
| 
								 | 
							
								to understand what is going on when using multiple libraries and/or
							 | 
						||
| 
								 | 
							
								frameworks, each with its own way to do class loading. Class
							 | 
						||
| 
								 | 
							
								loading should be simple and it is an ideal candidate for
							 | 
						||
| 
								 | 
							
								convention over configuration.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Overview
							 | 
						||
| 
								 | 
							
								--------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The Doctrine Common ClassLoader implements a simple and efficient
							 | 
						||
| 
								 | 
							
								approach to class loading that is easy to understand and use. The
							 | 
						||
| 
								 | 
							
								implementation is based on the widely used and accepted convention
							 | 
						||
| 
								 | 
							
								of mapping namespace and class names to a directory structure. This
							 | 
						||
| 
								 | 
							
								approach is used for example by Symfony2, the Zend Framework and of
							 | 
						||
| 
								 | 
							
								course, Doctrine.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For example, the following class:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    namespace MyProject\Shipping;
							 | 
						||
| 
								 | 
							
								    class ShippingStrategy { ... }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								resides in the following directory structure:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    src/
							 | 
						||
| 
								 | 
							
								      /MyProject
							 | 
						||
| 
								 | 
							
								        /Shipping
							 | 
						||
| 
								 | 
							
								           ShippingStrategy.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note that the name of "src" or the structure above or beside this
							 | 
						||
| 
								 | 
							
								directory is completely arbitrary. "src" could be named "classes"
							 | 
						||
| 
								 | 
							
								or "lib" or whatever. The only convention to adhere to is to map
							 | 
						||
| 
								 | 
							
								namespaces to directories and classes to files named after the
							 | 
						||
| 
								 | 
							
								class name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Usage
							 | 
						||
| 
								 | 
							
								-----
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								To use a Doctrine Common ClassLoader, you first need to load the
							 | 
						||
| 
								 | 
							
								class file containing the ClassLoader. This is the only class file
							 | 
						||
| 
								 | 
							
								that actually needs to be loaded explicitly via ``require``. All
							 | 
						||
| 
								 | 
							
								other classes will be loaded on demand by the configured class
							 | 
						||
| 
								 | 
							
								loaders.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    use Doctrine\Common\ClassLoader;
							 | 
						||
| 
								 | 
							
								    require '/path/to/Doctrine/Common/ClassLoader.php';
							 | 
						||
| 
								 | 
							
								    $classLoader = new ClassLoader('MyProject', '/path/to/src');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A ``ClassLoader`` takes two constructor parameters, both optional.
							 | 
						||
| 
								 | 
							
								In the normal case both arguments are supplied. The first argument
							 | 
						||
| 
								 | 
							
								specifies the namespace prefix this class loader should be
							 | 
						||
| 
								 | 
							
								responsible for and the second parameter is the path to the root
							 | 
						||
| 
								 | 
							
								directory where the classes can be found according to the
							 | 
						||
| 
								 | 
							
								convention mentioned previously.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The class loader in the example above would thus be responsible for
							 | 
						||
| 
								 | 
							
								all classes under the 'MyProject' namespace and it would look for
							 | 
						||
| 
								 | 
							
								the class files starting at the directory '/path/to/src'.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Also note that the prefix supplied in the first argument need not
							 | 
						||
| 
								 | 
							
								be a root namespace but can be an arbitrarily nested namespace as
							 | 
						||
| 
								 | 
							
								well. This allows you to even have the sources of subnamespaces
							 | 
						||
| 
								 | 
							
								split across different directories. For example, all projects under
							 | 
						||
| 
								 | 
							
								the Doctrine umbrella reside in the Doctrine namespace, yet the
							 | 
						||
| 
								 | 
							
								sources for each project usually do not reside under a common root
							 | 
						||
| 
								 | 
							
								directory. The following is an example of configuring three class
							 | 
						||
| 
								 | 
							
								loaders, one for each used Doctrine project:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    use Doctrine\Common\ClassLoader;
							 | 
						||
| 
								 | 
							
								    require '/path/to/Doctrine/Common/ClassLoader.php';
							 | 
						||
| 
								 | 
							
								    $commonLoader = new ClassLoader('Doctrine\Common', '/path/to/common/lib');
							 | 
						||
| 
								 | 
							
								    $dbalLoader = new ClassLoader('Doctrine\DBAL', '/path/to/dbal/lib');
							 | 
						||
| 
								 | 
							
								    $ormLoader = new ClassLoader('Doctrine\ORM', '/path/to/orm/lib');
							 | 
						||
| 
								 | 
							
								    $commonLoader->register();
							 | 
						||
| 
								 | 
							
								    $dbalLoader->register();
							 | 
						||
| 
								 | 
							
								    $ormLoader->register();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Do not be afraid of using multiple class loaders. Due to the
							 | 
						||
| 
								 | 
							
								efficient class loading design you will not incur much overhead
							 | 
						||
| 
								 | 
							
								from using many class loaders. Take a look at the implementation of
							 | 
						||
| 
								 | 
							
								``ClassLoader#loadClass`` to see how simple and efficient the class
							 | 
						||
| 
								 | 
							
								loading is. The iteration over the installed class loaders happens
							 | 
						||
| 
								 | 
							
								in C (with the exception of using ``ClassLoader::classExists``).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A ClassLoader can be used in the following other variations,
							 | 
						||
| 
								 | 
							
								however, these are rarely used/needed:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								-  If only the second argument is not supplied, the class loader
							 | 
						||
| 
								 | 
							
								   will be responsible for the namespace prefix given in the first
							 | 
						||
| 
								 | 
							
								   argument and it will rely on the PHP include_path.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								-  If only the first argument is not supplied, the class loader
							 | 
						||
| 
								 | 
							
								   will be responsible for *all* classes and it will try to look up
							 | 
						||
| 
								 | 
							
								   *all* classes starting at the directory given as the second
							 | 
						||
| 
								 | 
							
								   argument.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								-  If both arguments are not supplied, the class loader will be
							 | 
						||
| 
								 | 
							
								   responsible for *all* classes and it will rely on the PHP
							 | 
						||
| 
								 | 
							
								   include_path.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								File Extension
							 | 
						||
| 
								 | 
							
								--------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								By default, a ClassLoader uses the ``.php`` file extension for all
							 | 
						||
| 
								 | 
							
								class files. You can change this behavior, for example to use a
							 | 
						||
| 
								 | 
							
								ClassLoader to load classes from a library that uses the
							 | 
						||
| 
								 | 
							
								".class.php" convention (but it must nevertheless adhere to the
							 | 
						||
| 
								 | 
							
								directory structure convention!):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    $customLoader = new ClassLoader('CustomLib', '/path/to/custom/lib');
							 | 
						||
| 
								 | 
							
								    $customLoader->setFileExtension('.class.php');
							 | 
						||
| 
								 | 
							
								    $customLoader->register();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Namespace Separator
							 | 
						||
| 
								 | 
							
								-------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								By default, a ClassLoader uses the ``\`` namespace separator. You
							 | 
						||
| 
								 | 
							
								can change this behavior, for example to use a ClassLoader to load
							 | 
						||
| 
								 | 
							
								legacy Zend Framework classes that still use the underscore "_"
							 | 
						||
| 
								 | 
							
								separator:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    $zend1Loader = new ClassLoader('Zend', '/path/to/zend/lib');
							 | 
						||
| 
								 | 
							
								    $zend1Loader->setNamespaceSeparator('_');
							 | 
						||
| 
								 | 
							
								    $zend1Loader->register();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Failing Silently and class_exists
							 | 
						||
| 
								 | 
							
								----------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A lot of class/autoloaders these days try to fail silently when a
							 | 
						||
| 
								 | 
							
								class file is not found. For the most part this is necessary in
							 | 
						||
| 
								 | 
							
								order to support using ``class_exists('ClassName', true)`` which is
							 | 
						||
| 
								 | 
							
								supposed to return a boolean value but triggers autoloading. This
							 | 
						||
| 
								 | 
							
								is a bad thing as it basically forces class loaders to fail
							 | 
						||
| 
								 | 
							
								silently, which in turn requires costly file_exists or fopen calls
							 | 
						||
| 
								 | 
							
								for each class being loaded, even though in at least 99% of the
							 | 
						||
| 
								 | 
							
								cases this is not necessary (compare the number of
							 | 
						||
| 
								 | 
							
								class_exists(..., true) invocations to the total number of classes
							 | 
						||
| 
								 | 
							
								being loaded in a request).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The Doctrine Common ClassLoader does not fail silently, by design.
							 | 
						||
| 
								 | 
							
								It therefore does not need any costly checks for file existence. A
							 | 
						||
| 
								 | 
							
								ClassLoader is always responsible for all classes with a certain
							 | 
						||
| 
								 | 
							
								namespace prefix and if a class is requested to be loaded and can
							 | 
						||
| 
								 | 
							
								not be found this is considered to be a fatal error. This also
							 | 
						||
| 
								 | 
							
								means that using class_exists(..., true) to check for class
							 | 
						||
| 
								 | 
							
								existence when using a Doctrine Common ClassLoader is not possible
							 | 
						||
| 
								 | 
							
								but this is not a bad thing. What class\_exists(..., true) actually
							 | 
						||
| 
								 | 
							
								means is two things: 1) Check whether the class is already
							 | 
						||
| 
								 | 
							
								defined/exists (i.e. class_exists(..., false)) and if not 2) check
							 | 
						||
| 
								 | 
							
								whether a class file can be loaded for that class. In the Doctrine
							 | 
						||
| 
								 | 
							
								Common ClassLoader the two responsibilities of loading a class and
							 | 
						||
| 
								 | 
							
								checking for its existence are separated, which can be observed by
							 | 
						||
| 
								 | 
							
								the existence of the two methods ``loadClass`` and
							 | 
						||
| 
								 | 
							
								``canLoadClass``. Thereby ``loadClass`` does not invoke
							 | 
						||
| 
								 | 
							
								``canLoadClass`` internally, by design. However, you are free to
							 | 
						||
| 
								 | 
							
								use it yourself to check whether a class can be loaded and the
							 | 
						||
| 
								 | 
							
								following code snippet is thus equivalent to class\_exists(...,
							 | 
						||
| 
								 | 
							
								true):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    // Equivalent to if (('Foo', true)) if there is only 1 class loader to check
							 | 
						||
| 
								 | 
							
								    if (class_exists('Foo', false) || $classLoader->canLoadClass('Foo')) {
							 | 
						||
| 
								 | 
							
								      // ...
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The only problem with this is that it is inconvenient as you need
							 | 
						||
| 
								 | 
							
								to have a reference to the class loaders around (and there are
							 | 
						||
| 
								 | 
							
								often multiple class loaders in use). Therefore, a simpler
							 | 
						||
| 
								 | 
							
								alternative exists for the cases in which you really want to ask
							 | 
						||
| 
								 | 
							
								all installed class loaders whether they can load the class:
							 | 
						||
| 
								 | 
							
								``ClassLoader::classExists($className)``:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block:: php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <?php
							 | 
						||
| 
								 | 
							
								    // Equivalent to if (class_exists('Foo', true))
							 | 
						||
| 
								 | 
							
								    if (ClassLoader::classExists('Foo')) {
							 | 
						||
| 
								 | 
							
								      // ...
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This static method can basically be used as a drop-in replacement
							 | 
						||
| 
								 | 
							
								for class_exists(..., true). It iterates over all installed class
							 | 
						||
| 
								 | 
							
								loaders and asks each of them via ``canLoadClass``, returning early
							 | 
						||
| 
								 | 
							
								(with TRUE) as soon as one class loader returns TRUE from
							 | 
						||
| 
								 | 
							
								``canLoadClass``. If this sounds like it can potentially be rather
							 | 
						||
| 
								 | 
							
								costly then because that is true but it is exactly the same thing
							 | 
						||
| 
								 | 
							
								that class_exists(..., true) does under the hood, it triggers a
							 | 
						||
| 
								 | 
							
								complete interaction of all class/auto loaders. Checking for class
							 | 
						||
| 
								 | 
							
								existence via invoking autoloading was never a cheap thing to do
							 | 
						||
| 
								 | 
							
								but now it is more obvious and more importantly, this check is no
							 | 
						||
| 
								 | 
							
								longer interleaved with regular class loading, which avoids having
							 | 
						||
| 
								 | 
							
								to check each and every class for existence prior to loading it.
							 | 
						||
| 
								 | 
							
								The vast majority of classes to be loaded are *not* optional and a
							 | 
						||
| 
								 | 
							
								failure to load such a class is, and should be, a fatal error. The
							 | 
						||
| 
								 | 
							
								ClassLoader design reflects this.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								If you have code that requires the usage of class\_exists(...,
							 | 
						||
| 
								 | 
							
								true) or ClassLoader::classExists during normal runtime of the
							 | 
						||
| 
								 | 
							
								application (i.e. on each request) try to refactor your design to
							 | 
						||
| 
								 | 
							
								avoid it.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Summary
							 | 
						||
| 
								 | 
							
								-------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								No matter which class loader you prefer to use (Doctrine classes do
							 | 
						||
| 
								 | 
							
								not care about how they are loaded), we kindly encourage you to
							 | 
						||
| 
								 | 
							
								adhere to the simple convention of mapping namespaces and class
							 | 
						||
| 
								 | 
							
								names to a directory structure.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Class loading should be simple, automated and uniform. Time is
							 | 
						||
| 
								 | 
							
								better invested in actual application development than in designing
							 | 
						||
| 
								 | 
							
								special directory structures, autoloaders and clever caching
							 | 
						||
| 
								 | 
							
								strategies for class loading.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 |