Problem/Motivation
1) We've been discussing how and if to leverage PHP namespaces in Drupal 8, now that we have access to them in PHP 5.3. To date most of the discussion has been on tricking out modules-as-namespace, a discussion that has stalled as it is a much more complicated problem space than it seems at first glance.
2) Drupal has a lot of code. A lot of it is module code, but core itself is hardly svelte, either. That code has a cost, specifically, CPU and memory to load, parse, and hold onto it, especially code that we end up not actually using on a given page request. According to one report (which I've not verified myself yet) Drupal 7's bootstrap.inc, our "bare minimum system", is larger than common.inc was only a few releases ago. It's already been shown that reducing code weight can have a huge performance benefit, even on sites using APC.
3) Drupal is increasingly leveraging OO techniques and classes. On the whole, this is a good thing. Aside from the architectural potential for cleaner code, PHP allows us to lazy-load classes on-demand using autoloading. That is good for memory usage. Currently, however, Drupal's autoloader, the registry, is rather brittle. It works by building an index in the database of all available classes. That can be fast, but it also means it's not available until we have an active database connection. Rebuilding the registry index requires code scattered around between bootstrap.inc, common.inc, system.module (I know, really?), and a custom registry.inc.
4) We have a lot of places in Drupal where our code is total spaghetti, and as a result we're loading code we never actually need. The best example at the moment is in the locale system, where we have over 1000 lines of code we load and never use on 99% of pages. Fail.
5) We are already looking to incorporate code from 3rd party libraries as part of the WSCCI initiative. It is likely that we will do so as part of the Configuration Management Initiative as well. The main contenders right now are Zend Framework 2 and Symfony 2, both of which would allow us to pull in bits and pieces selectively when we need to.
6) Both the WSCCI and CMI initiatives are going to be generating a lot of new code, the majority of which will be Object Oriented based on current trends in both initiatives. It would be nice to know where to put that code, and know that we will not blow out our memory even worse by introducing it.
7) The PHP community is beginning to come to a consensus around the PSR-0 standard for PHP namespaces and class naming in PHP 5.3+. Both of the 3rd party libraries currently under consideration follow that standard. One of the advantages of it is that it includes an algorithm for a very simple autoload mechanism that requires no external resources (databases, etc.). There is even a standard implementation available under an extremely liberal license. Disclaimer: Although I was not involved in the creation of the PSR-0 standard, I am currently the nominal Drupal representative to the PHP Standards Working Group, such as it is.
Proposed resolution
I therefore propose that Drupal core elect to adopt the PSR-0 standard for class naming and namespace usage for "framework code" (which roughly corresponds to "things in /includes", but also includes stuff that lives in system.module for no good reason that we're already trying to move out of there in the #1224666: [meta] Unofficial Drupal 8 Framework initiative). This applies only to core framework code at this time, and does not include modules, hooks, theme keys, or anything else other than core classes. Procedural framework code in Drupal core is unaffected.
Secondly, I propose that any significant changes to our framework code move from procedural to PSR-0-compatible OO code where possible.
Thirdly, I propose that core framework code that is only rarely used and we currently lazy-load by hand we migrate over to an OO equivalent.
Advantages
This offers the following advantages:
1) We can easily integrate our code with 3rd party libraries. The nice thing about a standard for naming conventions is that, if followed, you won't collide. This includes both pulling 3rd party code into our code base as well as Drupal code being reused elsewhere. Cross-pollination is, generally, a good thing.
2) Any code that follows this convention we can lazy-load with only minimal bootstrapping. That is, it won't take up any memory or CPU time until and unless we need it, at which point it will "just work". Moreover, because there is no database dependency or rebuild process to worry about (as with the registry) it should be far more stable and less error prone. (The registry will remain in place to deal with module-based classes at this time.)
3) It provides us with a predictable standard and convention for code organization that we can clearly follow as we build out new code.
4) We are already partially following PSR-0 for class naming, minus namespaces and autoloading mechanisms. (This was a deliberate decision during Drupal 7 to incorporate those into our own coding conventions.)
5) Although at this time it does *not* address modules, at all, it does leave open the door for us to allow lazy-loading from module-provided directories at a later date. That might allow us to load non-core-provided framework code from modules in a non-crazy way, such as database drivers. I have not confirmed that is possible yet; at the moment it is just speculation on my part. But, this is an approach that is expandable later as we figure out how to do so.
6) It is not a Drupalism. It's a general PHP convention that any modern PHP developer either has been or will be exposed to sooner or later. That makes Drupal easier to learn because we're not inventing our own standards and conventions needlessly.
7) It provides an incentive for us to more cleanly separate framework-y code from non-framework-y, code, which is a goal that most of us already have.
8) Having this convention already established should help with including other autoloading PHP libraries in the /libraries/ directory, if they also use PSR-0.
Drawbacks
There are three potential drawbacks to this proposal, although all are, I think, mitigatable.
1) This requires developers using code that involves core OO to use namespace syntax. To be fair, PHP's namespace syntax is not its most beloved feature. However, this will be true no matter how we use namespaces, unless we avoid them entirely. Given their potential benefit I believe avoiding them would be foolish, so no matter what we do with namespaces we will have to, well, use namespaces. As above, this is not a Drupalism but a PHPism, and one that developers will run into anywhere, and can pick up from anywhere.
2) As I have expressed to the standards committee, class-per-file conventions made a lot of sense in Java but in non-APC-using PHP they can generate a fair bit more IO. This is not something we can easily avoid. However, I am willing to attempt this approach anyway for a couple of reasons. For one, newer Linux kernels have vastly improved disk caching, which should reduce the impact here. For another, well, based on how the rest of the PHP world is moving I lost this debate already. :-( No sense pouting about it. If you can't beat 'em, join 'em.
3) As I have expressed to the standards committee, Drupal's module implementation is in many ways totally incompatible with PSR-0, at least not without tremendous backending and evilness. We've already seen that trying to develop our own namespace convention for modules is quite a challenge, too. For that reason, this proposal actively avoids dealing with modules. As I note in that thread, however, our core framework code could and should be adapted to PSR-0, and improved in the process.
Remaining tasks
Assuming this is adopted, we would need to update our coding standards accordingly to reference the PSR-0 document.
First we'd include a copy of the SplClassLoader. This may be done as part of the inclusion of a 3rd party HTTP library anyway, because that library would be using PSR-0 in the first place.
We would then, incrementally, adapt code in /includes to PSR-0. No immediate change would be necessary, but we can migrate in bits and pieces as part of the various other cleanups going on. The database code in particular I would want to move post-November 1st to allow patches to still be compatible between Drupal 6 and Drupal 7 for a bit longer. (Dries recently indicated that we'll be breaking paths at that point anyway.)
(That would also be a good opportunity to investigate more fully separating the DB code from Drupal, which is a secondary goal several of us have had for a while.)
User interface changes
There would be no UI changes. This is entirely a code-level change.
API changes
The bulk of the API changes here would be the same as any other usage of namespaces. I will not restate how those work here as they are already documented in the PHP manual. (See link at the top of this summary.) As stated above, these are simply a part of the language.
Our vendor namepsace should be "Drupal", which for core would map to /includes/drupal (to differentiate from /includes/symfony or /includes/zend).
Each main subsystem should have a component, such as "Update" or "Registry" or "Configuration".
All components should be CamelCase, like classes.
We should avoid underscores in component namespaces.
Thus, we would have something like:
/includes/Symfony/Http/Whatever.php (stuff from the Symfony library here.)
/includes/Drupal/Locale/Bulder.php (class \Drupal\Locale\Builder)
/includes/Drupal/Cache/CacheInterface.php (interface \Drupal\Cache\CacheInterface)
/includes/Drupal/Cache/CacheDatabase.php (class \Drupal\Cache\Database, which implements \Drupal\Cache\CacheInterface)
And so on.
Conclusion
I know this post is long, but I believe it is, actually, a rather modest proposal. This will allow us to more fully integrate with the wider PHP community, provide a more performant and less error prone way to deal with our increasing code base, and provide clear guidance to core developers debating how to structure their code. It is also not incompatible with later expansion as we learn how to better leverage PHP 5.3's capabilities.
Comments
Comment #1
msonnabaum commentedI'm all for this. I've been using PSR-0 in drush deploy and it's been great.
As far as the drawbacks go, I don't think 1 is worth considering. The syntax is super ugly; So is php. We'll get over it.
2 is valid, but we dob't have to be tied to it. We could potentially not use that autoloader for a particular set of classes if we ended up with an I/O issue, but we have so few as it is I'm not sure that it would be a problem.
Comment #2
catchI've been thinking about this for a while as well, and got roughly to the proposal outlined here (i.e. that we could use PSR-0 for /includes and punt on everywhere else for now).
It would be great to be able to use an autoloader that doesn't depend on the database for includes. That makes the registry dependencies on the module system a bit less of an issue if we're just relying on it for module provided classes, while also making lots of things (potentially) not dependent on the database/installer just because they're autoloaded.
I'm not sure we want to autoload everything, it comes at a runtime cost for things that are always loaded anyway. So for example we might want to hard-require interfaces related to very low level systems like CMI, cache etc. to avoid autoloading those, but that's the same whether using PSR-0 or not in /includes. Needs a bit of thought but that's a discussion that probably warrants its own issue. There's definitely a lot of code in Drupal that it does make sense to autoload, that's currently included every request but used hardly ever, like a lot of common.inc.
Comment #3
Crell commentedcatch: There's certainly nothing that would prevent us from following this approach, and then also hard coding some require_once() calls into the code somewhere. It doesn't affect the IO, but does neatly skip the autoload overhead for those.
I may also be possible to detect what classes are used on a given page, save that list, and then front-load them on the next request. I've considered that in the past but never got around to trying to write it. That would also be entirely possible to layer on top of this underlying standard in a follow-up somewhere.
Comment #4
skwashd commentedIn principle I agree with this proposal. I think it will make it easier for developers to move between different frameworks.
My main concern is that this will lead to everything which is currently a function being moved to static methods in classes under includes/Drupal. I don't think such an approach would be much of an advancement, especially when considering the effort involved in the migration. This would probably provide a good opportunity to review mode of the include/ code.
Comment #5
Crell commentedskwashd: I agree. Simply turning foo_bar() into class Foo { static function bar() } would be the Wrong Way(tm) to do it. I'd be the first to shoot down such patches. :-) That's why any such refactoring can and should happen over time. Nothing here makes our current procedural code wrong, invalid, or unsupported. It just offers a better approach that we can migrate to over time, as we rewrite things for Drupal 8 as we are likely to do anyway, because it's Drupal. :-)
It may be the case that some things turn into a class with a few public methods and a few protected methods, all of which map mostly 1:1 to existing functions. As long as we're still instantiating an object and using it, that's OK, especially as a first cut. Others we may find make sense to move to multiple objects. That's something we can sort out case-by-case.
Comment #6
tobiassjosten commentedI think this is an excellent suggestion. More PHP interoperability to the people!
When implementing we could really benefit from the work done for Symfony2 with this. Their autoloader is really flexible in how it allows you to register namespaces, of any depth, and prefixes to specific directories. This enables you to autoload \Drupal\Form from /includes but \Pathauto\* from /sites/all/modules, for example.
Also the performance issue with increased IO has been solved in Symfony2 with the ClassCache component. If you really want to bother for developers running without APC, that is.
Comment #7
jherencia commentedGreat summary and explanation.
I think is a big improvement, needed to use other frameworks and to let other frameworks use parts of Drupal.
Comment #8
mparker17Subscribe
Comment #9
sunClosely related: #1239644: Define basic conventions for converting things into classes
Comment #10
jbrown commentedsubscribing
Comment #11
jbrown commentedComment #11.0
jbrown commentedInclude sample of non-Drupal class in the examples at the bottom.
Comment #12
Crell commentedtobiassjosten: Symfony's autoloader is, I believe, the standard SplClassLoader, which yes has that capability. It's kinda neat. :-) I have not looked at ClassCache so I cannot speak to it.
Also, yes, we absolutely want to continue to support non-APC users. They're a large constituency.
Comment #13
mfer commentedUm yeah, just do it.
Comment #14
yukare commentedJust a question about contributed modules, this change will not make it very incompatible with drupal 7? Just to say, we will need to change in a module about all calls to a drupal function because this.
Comment #15
franzsubscribe
Comment #16
catch@yukaru there are not really many core classes that contrib modules instantiate directly, if at all. Adopting this convention won't change that, but it's part of wider efforts to refactor a lot of the code in /includes that will mean api changes, which will be done in different issues to this one.
Comment #17
rcross commented+1
Comment #18
yukare commented@catch This change will reflect only in existing classes? I was thinking it will change like this, making all drupal oop:
node_load() becomes $node = new Drupal::node(); $node->load();
Comment #19
catch@yukare, this specific change is just adopting a convention. There may end up being a patch here, it might only be adding the autoloader Crell linked to though if at all.
There are likely to be a large number of other issues in Drupal 8 to convert various subsystems to using classes (although I doubt "all Drupal").
That includes some things linked from #1224666: [meta] Unofficial Drupal 8 Framework initiative, parts of initiatives like WSCCI and CMI, entities and various other bits.
However adopting PSR-0 as a standard and try to get newly added code to conform to it, does not get all of that done, and nor would it all be done in this issue (unless we wanted to try to have the first core issue with 10,000 followups ;)).
What it does do is help us to make some up front decisions about how our classes might be organised, and start to think about issues like how that organisation might impact things like unit testing and performance.
@Crell I think I'm going to start a new issue about front loading of classes, I have some rough ideas but don't want to take this one off topic.
Comment #20
chx commentedI think I miss something here. You skipped modules? Just like that? So how will contrib fit into this grand* scheme?
grand == totally braindead, usual PHP style.
Comment #21
catch@chx, there's a possibility we can apply PSR-0 to modules, and/or extend it to classes not provided by modules (i.e. in /libraries or similar). I know people have been thinking about this, not sure if there are concrete steps.
I think it's OK to start by using this for /includes where everything is predictable (and we should not be relying on the registry due to circular dependencies on system module...), and keep the registry for autoloading of classes in modules, at least as a first step.
Right now if you have a class that doesn't live in a module in Drupal 7, you have to hard require it in settings.php if it's needed before the registry is properly available, this doesn't make that situation any better or worse.
I opened #1241190: Possible approaches to bootstrap/front loading of classes.
Comment #22
Anonymous (not verified) commentedsubscribe. and big +1.
Comment #23
robertdouglass commentedMy favorite reason why this is a good idea: "6) It is not a Drupalism. It's a general PHP convention that any modern PHP developer either has been or will be exposed to sooner or later. That makes Drupal easier to learn because we're not inventing our own standards and conventions needlessly."
Comment #24
Crell commentedchx: I don't know how contrib will fit into this grand scheme. :-) The naive way to do so would be to point another SplClassLoader directive at the root of every module and have each module follow that same pattern internally. Is that a good idea? I don't know. It could get quite ugly, or it could be slow, or it could be the best option available. And still, that only really applies to classes which are still fairly uncommon in contrib outside of Views.
The point here is that we can start leveraging the benefits of this approach in core, where there are tangible, immediate benefits, and in the process learn how we may be able to leverage it in modules later. One step at a time.
Comment #25
jrbeemanI really like the direction this proposal is going. Like @robertDouglass and your point #6, I'm of the mindset that any changes we can start making that bring Drupal more in line with the broader landscape of PHP frameworks lowers the barrier to entry for experienced PHP developers that are new to Drupal.
Comment #26
plachsubscribe
Comment #27
bojanz commentedOh, nice.
Comment #28
Anonymous (not verified) commentedCrell: I would only suggest to use a separated directory for third party code, like Symfony and Zend. Symfony2 itself (the Symfony Standard Distribution) uses the
vendor/directory.I think Drupal would benefit a lot adopting PSR-0 and from already tested components available out there (Symfony2's HttpFoundation and Console components, Zend's Paginator component, e.g.). It would make the application more interoperable at the code level and also help boost adoption by OO developers.
Comment #29
Chris Johnson commented<curmudgeon>
It appears the Java-heads have invaded PHP-space and perverted it to their own ends. :-)
Crell says newer Linux kernels have vastly better [filesystem] caching, allowing this use of the filesystem as an autoload registry database to work well. Now, to me, Crell is a programming God. I take his word as gospel, in general. But having spent some years actually building Unix filesystems, I'm a bit skeptical about any filesystem getting that much faster. All that directory -> indirection -> costs -> real -> processing -> time, even if you are lucky enough to avoid having to read one of those meta data blocks off the disk. Not everybody has 16GB of RAM dedicated to their PHP server.
But if as Crell says, this is the way PHP is going and it's essentially a moot point, we might as well join them for consistency and ease of learning sake. I've got a 6-pack of your favorite beer that says D8 will be slower on the same hardware than D6, though.
</curmudgeon>
I agree with those who suggest we not autoload everything. That may be the key.
Comment #30
clemens.tolboom+1 aka subscribe
Comment #31
tobiassjosten commentedCrell: Not quite. The difference between the standard SplClassLoader and Symfony2's UniversalClassLoader is that the latter handles multiple namespaces/prefixes with fallbacks for default directories.
You could of course instantiate the standard one multiple times but then you wouldn't have the convenient fallback.
One important issue to discuss when talking autoloading and namespaces is PHP version requirement. Are we moving to require 5.3+ with Drupal 8?
Also I really don't think we should bend over backwards to facilitate good performance for people not running APC. They obviously don't care much about it, so why should we?
Comment #32
catchI'd like Drupal to run on non-APC sites but I don't really mind much beyond that - especially if it's a speed vs. memory trade-off, then non-APC sites tend to notice memory issues first - i.e. when hitting the limit.
I do care about installations that have APC enabled, but have not set apc.stat=0, because that's a very common case, so allowing people to remove the file i/o (and also reduce the number of files that need to be individually compiled and cached) is more important.
However I think the discussion about performance really belongs in #1241190: Possible approaches to bootstrap/front loading of classes, unless someone has a really strong objection to file-per-class to the point they'd try to block adopting the standard.
Comment #33
damien tournoud commentedFrankly, this is really the not interesting part of this. PSR-0 is just a stupid convention, adopting it for core framework classes is just a no-brainer. Let's do it.
The remaining part is what's interesting:
\Drupal\Modules\Commerce\CommerceEntityController). This could be closely coupled with my proposal of adding adrupal://streamwrapper for modules and themes (see #1185360: Refactor code loading and support loading from PHAR archives)Comment #34
sunWondering what we're going to do with stuff like StreamWrapperInterface, which is the base for our own DrupalStreamWrapperInterface -- only the latter is in the Drupal namespace.
Comment #35
damien tournoud commentedStreamWrapperInterface is in the global namespace but shouldn't be. It should be provided by PHP but shouldn't. The best we could do is place it under
Drupal\File\PHPStreamWrapperInterface.Comment #36
lsmith77 commentedI think you absolutely want to avoid "require_once" since it will impose disk i/o and the bootstrap file generation approach mentioned here http://drupal.org/node/1241190#comment-4835062 is a much better alternative for users that do not use a byte code cache.
Comment #37
Crell commentedChris Johnson: Hm, it seems my reputation exceeds me... :-)
I share your concern about IO performance. That's one of the key reasons I've long argued against class-per-file in PHP, as it is quite naive and not at all suited for the PHP space. But, I'm willing to bite my tongue and accept it here for a few reasons:
1) It has become a de facto PHP standard, for better or worse.
2) Operating systems are getting better.
3) Using that as a starting point, we can still optimize atop it with front-loading, precompiling include files, and other techniques. We're discussing possible options for that over in #1241190: Possible approaches to bootstrap/front loading of classes.
I don't drink beer, but several of us are doing our darnedest to see that you don't have to pay up. :-)
tobiassjosten: Yes, Drupal 8 is already confirmed as being PHP 5.3-required. And there is much rejoicing. Also, I didn't realize Symfony had extended the SplClassLoader. Nifty. Fortunately that's something we can swap in and out without really touching any other code in Drupal (yet another advantage of adopting such a standard).
That said, we care about performance for non-APC people because those non-APC people on cheap hosts will drop Drupal like a hot potato and install Wordpress instead the first time they see a "memory limit reached" error. I did exactly that myself with Typo3 a few years back: It started whining about needing more than 8 MB RAM to run its installer and at the time I didn't actually know what it was doing, just that it wouldn't run. So I deleted it and installed Drupal 4.6 instead. I can't imagine that same scenario is not playing out now on a daily basis, only it's Drupal that is losing. That's the sort of thing that keeps me up at night.
Comment #38
chx commentedYeah, let's do it and punt contrib. Agreed.
Comment #39
sdboyer commentedThis is great. Defining a base autoload point within core that follows PSR-0 will solve a number of our chicken-or-egg problems with regards to early-stage logic. As @Damien Tournoud said, this is a big fat no-brainer. Punting on contrib & plugin patterns is perfectly fine, as really, we'll want this in core pretty much no matter what, AND making this decision does not at all lock us in to a particular strategy elsewhere.
Let's be clear, though, that
SplClassLoaderis sorta awful and shouldn't be used, since we'd end up needing to register a *ton* of objects for additional namespaces/autoload points, and let them each perform their logic independently. An appreciably complex Drupal site could end up with dozens. No bueno. The sort of approach that Symfony2 has taken is a big step forward (@Crell - I mentioned this improved autoloader months ago :P), though that implementation might actually be *more* flexible than what we need, with some resulting IO costs.@tobiassjosten - it's an overstatement to say that symfony2's class caching has *solved* the io problem. It's a well-established technique, made easier to implement by a well-ordered structure for namespaces, but it can't just magically solve the problem of composing the cache content. We do, and will always (until PHP enters the new millenium and introduces a transparent, native system for opcode caching), have to care about sites without an opcode cache, which means we'll have to be crafty about how we build it. That said, the
ClassCollectionLoaderis another thing we can pick up and use to help solve this problem.Keeping my other performance ramblings in a response to #1241190: Possible approaches to bootstrap/front loading of classes.
Comment #40
pounardHello! Still didn't have the time to read all of this thread yet, but you should be aware I made an autoloader patch, based on my experience and a lot of duplicate code I had to make for many of my modules.
This autoloader handles both map-based and namespace-based PSR-0 autoloading, and seems to work fine. It also have reduced the bootstrap.inc from some code insanity. It is also loaded a lot before the actual autoloader, therefore could be used for cache backends and such later.
Please, for people that are interested go and see a more detailled description over there: #1254488: Map-based and namespace based PSR-0 compatible autoloader proposal
Comment #41
Crell commentedA patch to include the Symfony class loader in core (which is PSR-0) as part of bringing in the Symfony HttpFoundation library is included here: #1178246-34: Add Symfony2 HttpFoundation library to core.
Comment #42
donquixote commentedFrom what I understand so far about PSR-0,
I think we should not jump into this, as long as we don't have at least a rough plan how to deal with contrib modules, themes and libraries.
However:
I have yet failed to find any definition of the PSR-0 standard!
http://www.google.com/search?q=PSR-0
The first link,
http://groups.google.com/group/php-standards/web/psr-0-final-proposal?pli=1
sends me to a list of google group discussion topics, not a definition.
The rest is blog posts pointing to this not-helpful google groups page.
So, before I continue, can anyone post a link?
(ideally, also in the starting post)
Comment #43
donquixote commentedOk, seems this is what we currently know about PSR-0.
PSR-0, as far as I my understanding goes, is designed for arbitrarily nested directory structures, where class names match up with the directory path.
This is not the case for modules.
Drupal modules can be located in a variety of places, and only the system table (*) can tell us where to look for a specific one.
I don't think we should sacrifice this, only to conform with a standard.
So, given a class name, here is what our autoloader will need to do:
1. Detect / guess the module name -> class name prefix (**)
2. Locate the module directory -> system table (*)
3. Locate the class file within the module directory -> PSR-0 with the class name suffix.
(*) "system table"
This could as well be a cached version of the same data, stored in the filesystem.
(**) "class name prefix"
I would propose that class names in modules should be module name (lowercase) + "anything ucfirst".
This can be some_module_SomeClass, or some_module_Some_Class.
So, we get the module name by splitting off anything before the first underscore + uppercase character.
This is still somewhat in the spirit of PSR-0, but with the additional logic that is necessary for Drupal.
Comment #44
donquixote commentedmyself #43,
there is still a little problem with steps 2. and 3.
What if two modules share the same directory?
The autoloader would look for some_module_SomeClass in the same file as another_module_SomeClass, if both modules are in the same location. (that would be sth like $module_dir/classes/SomeClass.inc)
So, either
- two modules that participate in autoloading cannot be in the same location, OR
- modules can define alternate subfolders to look for classes, via their info file, OR
- the subfolder within the module dir does again contain the module name.
Such as,
sites/all/modules/some_and_another/some_module.info
sites/all/modules/some_and_another/another_module.info
sites/all/modules/some_and_another/lib/some_module/SomeClass.inc
sites/all/modules/some_and_another/lib/another_module/SomeClass.inc
Comment #45
catchWhy can't two modules be in the same location? All it needs to do is look for a file in that directory, and the system table will hold the location.
edited to add: I don't see any problem switching /includes to use this and punt on modules until a bit later. We have the registry available as a transition.
Comment #46
pounardAll you need is the system_list() and a convention. As you can find (for now) future core PSR-0 code under DRUPAL_ROOT./includes/<namespace> we could simply have for modules DRUPAL_ROOT./sites/all/modules/<modulename>/includes/<module_namespace> where the module_namespace could be formalized using a convention.
Then in order to use a PSR-0 autoloader over module we'd only need to know the module path (as in system_list() gives us, which is necessary to load the .module file anyway).
Comment #47
donquixote commentedpounard #46,
rather
sites/all/modules/<project_name>/includes/<module_namespace>where i don't get exactly what you mean with module_namespace.
Also, what is your plan about #43 point 1.?
Given a class name, how do you know which module that is?
Would you agree with my proposal of module name prefix, or do you have a different idea?
Comment #48
donquixote commentedcatch #45,
At least it would create some ambiguity.
A file
sites/all/modules/some_or_another/includes/Some/Class.incWhich class will this contain?
class some_module_Some_Class, or
class another_module_Some_Class ?
Or is this what we do with the "module_namespace" as in #46 ?
Comment #49
pounardLet's find an example:
content module on D6 was in cck folder, but when asking the system for the content module path, path returned was something like sites/all/modules/cck right?
Then let's take as convention that content module namespace would be Content (just simple camelcase: I take the module name, remove non alpha-numerical chars, and upcase the first letter: easy to do pragmatically, and really fast to) then the namespace path would be: sites/all/modules/cck/includes/Content/.
Now, let's say I have the Content\Field (Field class in the Content namespace) class, then the file would be: sites/all/modules/cck/includes/Content/Field.php
Just a simple sample, but working nicely.
EDIT: With this example, the real PSR-0 starts after sites/all/modules/<module>/includes and with the Symfony autoloader, you can assign path prefix at runtime to namespaces. So, basically, when I bootstrap modules I just have to addNamespaceWithPrefix('Content', 'sites/all/modules/cck/includes') to the autoloader, and basically the only information I need from database is sites/all/modules/cck which I already have in actual Drupal.
Now, allowing module to give another namespace by themselves could be done, either by their info file (but then need to go to database = bad) or either by a hook (if not implemented just assume default camelcased). Or any other mecanic can fit here, but PSR-0 default behavior for modules here is simple and pretty much failsafe (except maybe with the camelcase stuff because you could have a conflict with say cckmenu and cck_menu for example).
Re-EDIT: To allow namespaces to be changed from the module name, the .info line doesn't sound that bad, a column could be added to the system table (makes sense) and still no more SQL request would have to be done (and result can be cached anyway).
Now, to allow multiple namespaces per module, then multiple lines in .info file could be added, then definitely we would have to rely on cache (adding a new table just for namespaces is a lot of overengeneering.
In a perfect world, the D7 registry table would never have existed.
Re-Re-EDIT: I wrote "include" everywhere, but I'd really prefer "library" much more like pretty much all modern PHP frameworks (includes reminds me the good old PHP3, while library seems more the fashionable term now, and semantically a bit more correct IMHO since we are heavily orienting stuff to component driven architecture).
Comment #50
donquixote commentedOk, this seems to be the heart of your proposal, that makes it different from mine..
In my imagination, we would skip this additional subfolder level, and PSR-0 only the rest of the class name, that follows the module name (or module namespace).
All this seems to assume that we really move to 5.3 namespaces in D8, and not a "__" or "_hook_" separator.
This would of course make the point 1. from #43 quite easy to solve.
Fine for my taste, but.. for me it did not look like people would come to an agreement soon.
I guess we should prepare sth for both scenarios - with namespaces, and without them.
I wonder, do we need to CamelCase everything?
Is the perceived "lowercase for classes looks wrong" a higher value than having the literal module name for grep and find-replace?
With namespaces:
I don't see any problem about having lowercase + underscore in namespace names. Or does an undercore mean "subfolder" even for namespace names?
Without namespaces:
some_module_SomeClass or some_module_SomeClass does make it quite easy to detect what is the module name, and what is the class name.
(EDIT:)
With SomeModule_Some_Class, ok, you look at the first fragment..
In both cases, you can nameclash with 3rd party libraries.
With the first approach, you can even nameclash with other modules (but this is not a new problem), since PHP (unlike the autoloader!!) is case-insensitive.
Comment #51
pounardUnderscore means subfolder in PSR-0 for PHP5.2, actually most frameworks will ship with autoloaders that will treat both \ and _ as directory separator.
Comment #52
donquixote commentedOk...
As far as I understand, there is not really the "absolute" definition of PSR-0 yet, but rather some ideas, and "this is how framework X implements it".
I do not see why we can not add our own Drupal-specific autoloading logic, that goes beyond basic PSR-0, in aspects where Drupal is just different.
So, the Drupal autoloader would split a class name (+ namespace), use the first part for module detection, and only the second part for PSR-0 within that module dir.
The "underscore = subfolder" would only be relevant for the second part of the class name. In the first part, it is simply part of the module name, and that's it.
---------------
Ok, all that said:
I think I am now convinced that there are viable solutions available for autoloading in modules, and we are not running into a dead end.
The exact implementation can be discussed elsewhere.
So, we can get back to "core framework classes".
Discussion about modules can move here :)
#1290658: Move all module-provided classes to PHP namespaces (PSR-0 or similar), and autoload them without the registry [policy, no patch]
Comment #53
donquixote commentedI wonder if this is actually a good idea, to put parts of symfony (or other 3rd party stuff) into includes? Should this not live in sites/all/libraries?
Also, we should be careful including 3rd party stuff in Drupal. If Drupal core depends on a class namespaced as "symfony", then what happens if a user wants to install a different version of symfony in sites/all/libraries? Firstly, it will result in the same class being defined twice, secondly the conflict between versions..
(absolutely not sure if this is what Crell intended with the example)
Comment #54
lsmith77 commentedyou guys might also want to keep an eye out for composer. this is currently being worked on to provide packaging for php libs. i don't know exactly if they have plans to deal with different versions of the same lib:
https://github.com/composer
Comment #55
pounard@#53 As Symfony is going to be a core dependency, its component should live side by side with core API: sites dir is for external or contrib code on which the core doesn't rely and not the opposite.
A bit off-topic: I think it'd make sense to move this whole new PHP5.3 API into its own dir such as DRUPAL_ROOT/library to separate it from the legacy includes dir, which name is an inheritance from the past pure PHP3/4 procedural/scripting code style. Library dir name does semantically fit with a library/component based approach.
Comment #56
Crell commentedI think we're settled on core classes already. We're just waiting for #1178246: Add Symfony2 HttpFoundation library to core to get committed. I don't see any PSR-0 "variants" like donquixote describes, but if such exists, we should stick with the "variant" used by Symfony since we'll be using Symfony's class loader and some libraries.
Contrib we sort out "later" (eg, in the linked issue).
Comment #57
Crell commented#1178246: Add Symfony2 HttpFoundation library to core was just committed today, so marking this fixed. Further discussion can happen in new issues.
Comment #58
catchFollow-ups:
#1320648: Meta: start converting existing core classes to PSR-0 [policy, no patch]
#1320650: Stop scanning /includes with the registry
Comment #59
nielsvm commentedsubscribing
Comment #60.0
(not verified) commentedFixed link to framework initiative.
Comment #61
mossy2100Can I suggest that we use the directory name "classes" instead of "vendor" or "includes". Since, you know, it contains classes. Like the "modules" directory contains modules and the "themes" directory contains themes.
I've taught numerous workshops on OOP in PHP5 and have written many classes for use with Drupal 6 and 7 apps. Can I also suggest that we mandate a standard extension of ".class.php" for all classes. I realise this is a slight variation from PHP, but I believe it adds semantic value to the filename and is preferable (in terms of clarity) to alternatives such as ".inc".
Comment #62
mossy2100Can I suggest that we use the directory name "classes" instead of "vendor" or "includes". Since, you know, it contains classes. Like the "modules" directory contains modules and the "themes" directory contains themes.
I've taught numerous workshops on OOP in PHP5 and have written many classes for use with Drupal 6 and 7 apps. Can I also suggest that we mandate a standard extension of ".class.php" for all classes. I realise this is a slight variation from PSR-0, but I believe it adds semantic value to the filename and is preferable (in terms of clarity) to alternatives such as ".inc".
Comment #63
pounardPSR-0 does not only serve the purpose of being a project standard, but also helps unifying with Symfony, the chosen underlaying framework and allows us to use their tools (autoloaders and such) without having to extend or rewrite them. Not only Symfony, but a lot of other frameworks are already using this standard and a lot of others will in the future too. I don't think changing that is a good idea for us now, using PSR-0 also means accepting that PHP has standard and raise the understanbility of our own software. Furhter, it's too late since alsmot all Drupal code already has been ported.
Comment #64
pounardComment #65
Crell commentedComment #65.0
Crell commentedPSR-0 link is broken (http://groups.google.com/group/php-standards/web/psr-0-final-proposal?pli=1).
Changing link to: https://gist.github.com/1234504.