<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[
<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[
Zend_Loader_Autoloader is a component for autoloading registered namespaces & managing arbitrary autoload callbacks.Zend Framework: Zend_Loader_Autoloader Component Proposal
Proposed Component Name
Zend_Loader_Autoloader
Developer Notes
http://framework.zend.com/wiki/display/ZFDEV/Zend_Loader_Autoloader
Proposers
Ralph Schindler
Matthew Weier O'Phinney
Zend Liaison
Matthew Weier O'Phinney
Revision
1.0 - 1 January 2008: Initial Draft.
1.1 - 17 December 2008: Initial Draft. (wiki revision: 10)Table of Contents
1. Overview
2. References
3. Component Requirements, Constraints, and Acceptance Criteria
4. Dependencies on Other Framework Components
- Zend_Exception
- Zend_Loader
5. Theory of Operation
The component is available for use by applications that might use multiple autoloaders (for example modules that might require the use of a custom autoloader).
Problems this autoloader attempts to solve:
- Ability to autoload only specific namespaces
- Ability to handle arbitrary autoload callbacks. spl_autoload is problematic to manipulate when using object instance methods
- Ability to manage the ordering of the autoloader stack
6. Milestones / Tasks
- Milestone 1: [DONE] Requirements written and published as a proposal
- Milestone 2: [DONE] Working prototype checked into a public repository supporting the use cases
- Milestone 3: Community review of proposal
- Milestone 4: Acceptance of proposal
- Milestone 5: Unit tests exist, work, and are checked into icubator
- Milestone 6: Initial documentation exists
- Milestone 7: Approval for trunk
7. Class Index
- Zend_Loader_Autoloader
- Zend_Loader_Autoloader_Interface
8. Use Cases
| UC-01 |
|---|
Standard usage; no extra libraries:
| UC-02 |
|---|
Register additional namespaces:
| UC-03 |
|---|
Register an arbitrary autoloader callback:
| UC-04 |
|---|
Use the autoloader interface to define an autoloader, and register an instance with the global autoloader.
| UC-05 |
|---|
Suppress warnings.
| UC-06 |
|---|
Use autoloader as a fallback autoloader (i.e., will load any namespace):
9. Class Skeletons
| Zend_Loader_Autoloader |
|---|
| Zend_Loader_Autoloader_Interface |
|---|
22 Comments
comments.show.hideJul 18, 2008
Bryce Lohr
<p>Could you provide a little more background on the problems this component solves? Even with the descriptions present, I still don't understand the benefit this is trying to bring.</p>
<p>Also, you state that this will <strong>not</strong> use <code>spl_autoload_register</code>, yet this line from the class skeleton seems to indicate otherwise:</p>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
// autoload method to be registered with php's spl_autoload_register
public function autoload($class);
]]></ac:plain-text-body></ac:macro>
<p>Is this meant as a user-land tool, or for internal framework use? ZF itself doesn't currently use autoload; does this proposal indicate a change in direction on that?</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>Hopefully the changes to the proposal now answer your questions. We will be using spl_autoload_register to register Zend_Loader_Autoloader::autoload(), but then have Z_L_A maintain its own registry of autoloaders. The rationale behind it is to simplify the process of registering autoloaders, but also to work around deficiencies in spl_autoload (which often has trouble when re-registering instance methods as callbacks).</p>
Dec 17, 2008
Bryce Lohr
<p>Yes, it's much clearer now; thank you!</p>
<p>Also, shouldn't setFallbackLoader() be a setter for an arbitrary fallback loader, rather than a flag? Or am I just misunderstanding what it's about?</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>setFallbackLoader() is a flag to indicate that <em>any</em> namespace can be matched by the autoloader. For instance, if you're using PEAR, you may want to enable this as PEAR doesn't currently enforce a top-level namespace.</p>
Dec 17, 2008
Rob Allen
<p>It's non-intuitive to use Zend_Loader_Autoloader::getInstance(); to register with spl_autoload. Better would be: Zend_Loader_Autoloader::registerAutoload();</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>Noted. Thanks!</p>
Dec 17, 2008
Rob Allen
<p>How does this relate to Zend_Loader? Or more specifically, what is the benefit of having Zend_Loader::registerAutoload() and Zend_Loader_Autoloader? Can the registry of autoloaders be added to Zend_Loader? Will Zend_Loader's registerAutoload() be deprecated?</p>
<p>I could equally imagine something like:<br />
$myAutoloader = new My_Autoloader('/path/to/classes', 'My_Stuff');<br />
Zend_Loader::pushAutoloader($myAutoloader);</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>The idea would be to deprecate Zend_Loader::registerAutoload() in favor of Zend_Loader_Autoloader. (We may even have it proxy to it.)</p>
<p>The benefits are several:</p>
<ul>
<li>Better error handling capabilities. This has been a constant issue with using Zend_Loader::registerAutoload(), as some people want to have parse errors bubble up, and others don't want warnings when files aren't found (as they have other autoloaders that can map the class to a file). By having a central autoloader registry, we can make this <strong>much</strong> cleaner.</li>
<li>Ability to restrict which namespaces are in use. (Leads to faster lookups, and also ensures that developers are explicit about selecting third party libraries for use with their application. That said, we allow it to act as a fallback handler, too.)</li>
<li>Zend_Loader_Autoloader would use Zend_Loader::loadClass() internally. However, this keeps a separation of concerns: one has a mechanism for loading classes and files, the other performs autoloading and handles an autoloader registry.</li>
</ul>
<p>Finally, as noted in the proposal, this also works around issues we've noticed with spl_autoload_register when used with arbitrary instance method callbacks.</p>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
> I could equally imagine something like:
> $myAutoloader = new My_Autoloader('/path/to/classes', 'My_Stuff');
> Zend_Loader::pushAutoloader($myAutoloader);
]]></ac:plain-text-body></ac:macro>
<p>I'd like to keep a clean separation of concerns, which is why we're suggesting a new class.</p>
Dec 17, 2008
Rob Allen
<p>Another question! Why are there namespace related functions here? Surely the instances of Zend_Loader_Autoloader_Interface worry about such things?</p>
<p>Also, if you are going to have a pushAutoloader(), surely you would have a popAutoloader()?</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>There's no reason to have a separate autoloader for each namespace when the implementation is the same. The motivation is to speed up lookups and prevent unnecessary lookups (if the namespace isn't in the list, just return false immediately instead of trying to load a file).</p>
<p>Autoloaders implementing the interface will be used for custom autoloading – such as with the resource autoloader – where there is not a 1:1 class/filename relation.</p>
<p>And yes, you're right, there should be popAutoloader and shiftAutoloader methods – I'll update the proposal to reflect that.</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>Please see my comment to Till below – pop and shift don't make much sense, as you typically need to <em>selectively</em> remove an autoloader, but when adding you need to indicate <em>where</em> in the stack to add it.</p>
Dec 17, 2008
Till Klampaeckel
<p>I don't like the push* method names. I know they come from array_push and array_pop, but most other components in the framework use addFoo() and removeFoo() style method names.</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>The problem with using add/remove is that with autoloading, we actually need to maintain a stack to ensure order – you need to be able to specify which autoloaders have precedence. I'm actually thinking unshift/push are all we need, as remove would allow you to choose <strong>which</strong> autoloader to remove, while unshift and push add to the stack, but tell you <strong>where</strong> to add them. (btw, removeAutoloader() <strong>is</strong> in the class skeleton)</p>
Dec 17, 2008
Till Klampaeckel
<p>I thought push just adds to the end. No idea how the order is maintained there. From my understanding addAutoloader() would do the same?</p>
<p>I find it somewhat confusing with all the autoload goodness offered by the framework already. E.g. I think Zend_Loader has multiple methods that revolve around autoload'ing - point taken some parts are hard to fit into it so I see (and read) what you're trying to solve here.</p>
<p>But can you detail how Zend_Loader will play a role in this, or aim those proposals to eventually replace Zend_Loader? </p>
<p>Also how many autoloaders will I typically need for an app? E.g. for controllers, models, forms, library. An autoload for each one of them?</p>
<p>Last but not least, can you show code so we can have a look how exactly you're solving the loading part?</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>re: push: you obviously missed the unshiftAutoloader() method, which <em>prepends</em> to the stack. We could maybe make these "prepend" and "append" instead of "unshift" and "push".</p>
<p>Zend_Loader has two methods related to autoloading. autoload() is the actual callback used for autoloading, and registerAutoload() registers with spl_autoload(). The idea is to deprecate these and move autoloading into its own class. In the short term, registerAutoload() would actually register Zend_Loader_Autoloader, and Zend_Loader::autoload() would receive a deprecation notice.</p>
<p>As for how many autoloaders would you need: one per module in most cases. Take a look at the Resource autoloader proposal, as it shows the proposed implementation. </p>
<p>Finally, the References section has a link to code I've developed in the pastebin demo.</p>
Dec 17, 2008
Lars Strojny
<p>Two things: first, I'm not sure if the API should be cluttered with stuff dealing with the internal stack of autoloaders. Maybe having a separate collection object for autoloaders and using it in the following way fits better:</p>
<p>Zend_Loader_Autoload::getInstance()<span style="text-decoration: line-through;">>getStack()</span>>push(new MyAutoLoader());</p>
<p>What's the route to forward compatibility with 5.3th namespaces?</p>
Dec 17, 2008
Matthew Weier O'Phinney
<p>The collection object makes sense, though it adds more complexity. That said, I'll definitely consider it.</p>
<p>As for 5.3 namespaces, we'll address that more in the coming months, but both Zend_Loader and this component will need some minor changes to facilitate usage with 5.3. We need a final API on namespaces before we even consider doing so – and even though namespaces are considered a "done deal" for 5.3, until there's a beta out, the API can change.</p>
Dec 17, 2008
Rob Allen
<p>Given that Zend_Loader_Autoload's only point is to be a collection of autoloaders, surely a collection object is redundant?</p>
Dec 18, 2008
Lars Strojny
<p>As far as I understand it, it's the management interface to a collection of autoloaders, not a collection itself.</p>
Jan 08, 2009
Matthew Weier O'Phinney
<ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Acceptance Criteria</ac:parameter><ac:rich-text-body>
<p>This proposal is accepted for immediate development in the standard incubator, with the following additions:</p>
<ul>
<li>A Collection object (most likely an SplStack) should be used for handling the autoloaders</li>
<li>Autoloaders should allow optionally specifying a namespace prefix. The implementation should then attempt to match namespace prefixes with specific autoloaders, and try only those autoloaders when attempting to find a match.</li>
</ul>
</ac:rich-text-body></ac:macro>
Apr 08, 2009
Rick
<p>Sorry, double post.</p>
Apr 08, 2009
Rick
<p>I have 2 questions regarding the following lines of the "getClassAutoloaders" method:</p>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
foreach (array_keys($this->_namespaceAutoloaders) as $ns) {
if ('' == $ns)
if (0 === strpos($class, $ns))
}
]]></ac:plain-text-body></ac:macro>
<p>1. Edited: A minor point, but couldn't it be changed to:</p>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$autoloaders = $this->getNamespaceAutoloaders($ns);
]]></ac:plain-text-body></ac:macro>
<p>2. Having a break; after the first match would exclude other potential namespace matches. Remove the break; and my first point doesn't apply.</p>