rfc:autoloader_error_handling
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
rfc:autoloader_error_handling [2011/11/16 21:57] – akkie | rfc:autoloader_error_handling [2011/11/24 22:17] – akkie | ||
---|---|---|---|
Line 10: | Line 10: | ||
I use the following terms in the subsequent examples: | I use the following terms in the subsequent examples: | ||
* project - for every piece of software(library, | * project - for every piece of software(library, | ||
- | * autoloader(Exception) - for an autoloader | + | * autoloader(Exception) - for an autoloader |
- | * autoloader(Error) - for an autoloader | + | * autoloader(Error) - for an autoloader |
- | * autoloader(Silent) - for an autoloader | + | * autoloader(Silent) - for an autoloader |
- | * policy(Exception) - for a policy | + | * policy(Exception) - for a policy |
- | * policy(Error) - for a policy | + | * policy(Error) - for a policy |
- | * policy(Silent) - for a policy | + | * policy(Silent) - for a policy |
===== Introduction ===== | ===== Introduction ===== | ||
- | If working in a multi project environment, | + | If working in a multi project environment, |
===== Problems ===== | ===== Problems ===== | ||
- | Note: | ||
- | | + | **Notes: |
* If an project registers its own autoloader than it uses automatically the same policy. I won't attribute this in the examples. | * If an project registers its own autoloader than it uses automatically the same policy. I won't attribute this in the examples. | ||
* All autoloader implementations aren't compatible. They cannot load classes across projects. | * All autoloader implementations aren't compatible. They cannot load classes across projects. | ||
* The notation autoloader(...)-1 describes the position of the loader in the stack. The first position in this example. | * The notation autoloader(...)-1 describes the position of the loader in the stack. The first position in this example. | ||
- | | + | **Examples: |
+ | |||
+ | | ||
* PHPUnit registers several autoloaders(Silent)2-10. The test suite uses an autoloader(Exception)-1. Same problem here. None of the PHPUnit classes can be loaded. A workaround would be to register the test suite autoloader as last. | * PHPUnit registers several autoloaders(Silent)2-10. The test suite uses an autoloader(Exception)-1. Same problem here. None of the PHPUnit classes can be loaded. A workaround would be to register the test suite autoloader as last. | ||
* A third party project registers its own autoloader(Exception)-1. The main project registers its own autoloader(Error)-2. The same problem as in the first example. But here exists no workaround because the two autoloaders mutually interfere. | * A third party project registers its own autoloader(Exception)-1. The main project registers its own autoloader(Error)-2. The same problem as in the first example. But here exists no workaround because the two autoloaders mutually interfere. | ||
Line 39: | Line 41: | ||
* Autoloaders may not interfere with each other | * Autoloaders may not interfere with each other | ||
+ | * If I have an autoloader(Error) in project A and an autoloader(Exception) in project B then both autoloaders must be processed. Only if both autoloaders fails to load the class, a decision should be made on how the autoloader mechanism behaves. | ||
+ | * For the main project it should be possible to manipulate the behavior of an autoloader | ||
+ | * If an autoloader(Silent) fails to load a class from a sub project, it should be possible to change the behavior of the autoloader to the effect that an exception will be thrown. This could be necessary to shutdown the application in an ordered way. | ||
+ | * On the other side it should be possible to manipulate an autoloader(Exception) or autoloader(Error), | ||
+ | * Sub projects(libraries, | ||
+ | * If I have defined an autoloader(Exception) in my sub project, then it must be guaranteed that I can catch(in this sub project) exceptions thrown by this autoloader. Even if the main project changes the behavior of this autoloader. | ||
+ | |||
+ | ===== Solution ===== | ||
+ | |||
+ | **Stop interference between autoloaders** | ||
+ | |||
+ | To fulfill the first requirement the engine must catch all errors or exceptions until all autoloaders are executed. If the class could be loaded by one of the autoloaders, | ||
+ | |||
+ | **Allow manipulation of autoloader results** | ||
+ | |||
+ | After a class couldn' | ||
+ | |||
+ | As example: If the user sets the policy to: | ||
+ | |||
+ | * **POLICY_EXCEPTION**, | ||
+ | * **POLICY_ERROR**, | ||
+ | * **POLICY_SILENT**, | ||
+ | |||
+ | ** Sub projects must rely on their own policy** | ||
+ | |||
+ | A possible solution for this point would be the introduction of a namespace based autoloader. So a subproject can use its own autoloader mechanism which doesn' | ||
+ | |||
+ | |||
+ | ===== The big picture ===== | ||
+ | |||
+ | It would be a huge BC break to implement all this features. Therefore a new class could be implemented for the spl extension. The next code example describes a possible interface for this class. | ||
+ | <code php> | ||
+ | |||
+ | interface SplClassAutoloader { | ||
+ | |||
+ | const POLICY_ERROR = 1; | ||
+ | const POLICY_EXCEPTION = 2; | ||
+ | const POLICY_SILENT = 3; | ||
+ | |||
+ | /** | ||
+ | * Creates a new autoloader instance. | ||
+ | * | ||
+ | * @param int $policy The policy for this autoloader. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | /** | ||
+ | * Registers a namespace for this autoloader. | ||
+ | * | ||
+ | * @param string $namespace The namespace to register. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | /** | ||
+ | * Unregisters a namespace for this autoloader. | ||
+ | * | ||
+ | * @param string $namespace The namespace to unregister. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | /** | ||
+ | * Registers a class loader implementation. | ||
+ | * | ||
+ | * @param SplClassLoader $classLoader The class loader to register. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | /** | ||
+ | * Unregisters a class loader implementation. | ||
+ | * | ||
+ | * @param SplClassLoader $classLoader The class loader to unregister. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | /** | ||
+ | * Hook the autoloader into the engine. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | /** | ||
+ | * Remove the autoloader from the engine. | ||
+ | */ | ||
+ | | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | The class loader interface should have only one method. | ||
+ | |||
+ | <code php> | ||
+ | |||
+ | interface SplClassLoader { | ||
+ | |||
+ | /** | ||
+ | * Loads the given class. | ||
+ | * | ||
+ | * @param string $className | ||
+ | * @return boolean True if the class could be loaded, false otherwise. | ||
+ | */ | ||
+ | | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | ===== Examples of usage ===== | ||
+ | |||
+ | Autoloading PHPUnit | ||
+ | |||
+ | <code php> | ||
+ | |||
+ | $autoloader = new SplAutoloader(SplAutoloader:: | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | ... | ||
+ | $autoloader-> | ||
+ | </ | ||
+ | |||
+ | Autoloading multiple namespaces | ||
+ | |||
+ | <code php> | ||
+ | |||
+ | $autoloader = new SplAutoloader(SplAutoloader:: | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | |||
+ | </ | ||
+ | |||
+ | Autoloading multiple projects | ||
+ | |||
+ | <code php> | ||
+ | |||
+ | $autoloader = new SplAutoloader(SplAutoloader:: | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | |||
+ | $autoloader = new SplAutoloader(SplAutoloader:: | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | |||
+ | </ | ||
+ | |||
+ | Global autoloader(Load all classes for which no namespaced autoloader exists) | ||
+ | |||
+ | <code php> | ||
+ | |||
+ | // Namespaced | ||
+ | $autoloader = new SplAutoloader(SplAutoloader:: | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
- | This means, for example: | + | // Global |
- | If I have an autoloader(Error) in project A and an autoloader(Exception) in project B then all both autoloaders must be processed. Only if both autoloaders fails to load the class a decision should be made how the procedure continues. | + | $autoloader = new SplAutoloader(SplAutoloader:: |
+ | $autoloader-> | ||
+ | $autoloader-> | ||
+ | $autoloader-> | ||
- | * For the main project it should be possible to manipulate the behavior of an autoloader | + | new \PHPUnit_Foo(); |
+ | new \foo\Bar() // Triggers global | ||
- | This means, for example: | + | </ |
- | If an autoloader(Silent) fails to load a class from a sub project, it should be possible to change the behavior of the autoloader to the effect that an exception will be thrown. This could be necessary to shutdown the application in an ordered way. | + | |
+ | ===== Patch ===== | ||
+ | No patch available. | ||
===== Changelog ===== | ===== Changelog ===== |