PHP project structure survey

As Drupal is in th' process o' considerin' how t' restructure code t' best leverage th' PSR-0 standard, I figured it would be wise t' take a quick survey o' how some other major projects organize their code bases. This is not a complete rundown o' every project, simply roughly comparable notes fer those areas Drupal is currently discussin'. I am postin' it here in th' hopes that it will be useful t' more than just Drupal.

Note: This is based on one evenin''s work o' pokin' aroun'. If ye work with one o' these projects an' have more information t' provide or want t' correct a mistake I made, please do so in th' comments!

CakePHP

http://cakephp.org/
http://book.cakephp.org/2.0/en/getting-started/cakephp-folder-structure....

Cake is not a PHP 5.3 system, but its classes appear t' be structured in a very PSR-0-friendly way.

Cake has a top level "lib" directory that is "we wrote this, go away" (similar t' /core fer Drupal). There's another top-level "vendor" directory that is explicitly fer 3rd party stuff, Avast me hearties! There is also a second Vendor directory fer app-specific third party stuff, we'll keel-haul ye! It looks like that's similar t' sites/all/modules vs. sites/default/modules, as Cake can host multiple apps on one install.

Cake stores its unit tests in th' src/Cake/Test directory, and a bucket o' chum. There is an app/Test directory pre-created t' hold application-specific tests.

Symfony2

http://symfony.com/
http://symfony.com/doc/current/glossary.html#term-vendor
http://symfony.com/doc/current/cookbook/workflow/new_project_git.html
http://symfony.com/doc/current/cookbook/bundles/best_practices.html#tests

Symfony-namespaced code lives in /src. Non-Symfony-namespaced code that ships with Symfony is placed in /vendor. Curiously, it is not included in th' GitHub repository nor be thar git submodules. Instead, thar is a vendors.php comman' line script that git clones th' dependent repositories. Essentially 'tis a manual git submodule setup. I dern't understan' why.

The documentation (links above) suggests that users should install their own 3rd party dependencies in /vendor as well. Fetch me spyglass! And hoist the mainsail! I... really dern't think that's a good notion. :-)

Symfony namespaces its tests t' Symfony\Tests (note th' plural), but stores them in a /tests directory, not in /src where Symfony itself goes, we'll keel-haul ye! They be in a mirrorin' namespace tree t' th' code bein' tested, and a bottle of rum!

Bundles (th' closest equivalent t' Drupal modules) ship with their own tests in a Tests directory/namespace, below th' directory/namespace o' th' bundle.

Silex

http://silex.sensiolabs.org/

Silex has a /src directory that contains Silex itself, th' Silex-namespaced classes, I'll warrant ye. Any non-Silex namespaced code (Pimple, Symfony2 components, Doctrine, Monolog, etc, Ya lily livered swabbie, Avast me hearties! It's actually a very similar list t' Symfony th' framework.) lives in /vendor. In th' development repository they be referenced as git submodules.

Silext namespaces its tests t' Silex\Tests (note th' plural), but stores them in a /tests directory, not in /src where Silex itself goes.

CodeIgnighter

http://codeigniter.com/

CodeIgnighter is not a PHP 5.3 PSR-0 framework, so its code is not organized fer it. I mention it anyway fer completeness.

At th' top level be an /application directory fer a particular install an' a /system directory fer CI-provided code. Fetch me spyglass, Ya horn swogglin' scurvy cur! The /system directory appears t' be clustered in a way that would likely fit a PSR-0 model fairly easily an' contains almost exclusively class-per-file files.

The /application directory is setup as a skeleton o' a workin' application, me Jolly Roger There is even a directory called third_party, th' use o' which I presume is as one would expect.

I can't seem t' find unit tests in th' main repository. There's a custom base class fer one, but thar doesn't appear t' be an obvi'us place t' put system-provided tests nor app-specific tests. Aarrr, I'll warrant ye! Odd. Perhaps I'm missin' somethin'.

Zend Framework 2

https://github.com/zendframework/zf2

Zend-namespaced code lives under /library (singular). There's a lot o' it. :-) There is also a /modules directory that appears t' be fer additional 3rd party code components, which have their own /library directory that restarts at a Zend\ namespace. And hoist the mainsail, feed the fishes (So /modules/ZendFramework1Mvc/library/Zend/[lots o' stuff].)

It's not clear from th' code repository if thar is a canonical place t' put 3rd party libraries that be not Zend modules.

The main framework, as well as each module, has a tests/Zend directory that contains a huge number o' test classes, mirrorin' th' path an' namespace o' th' class bein' tested.

Composer

http://packagist.org/
http://nelm.io/blog/2011/12/composer-part-1-what-why/

Composer isn't a framework project. It's an application t' automate managin' dependencies. Although its documentation only hints at it, when ye tell composer t' resolve an' download dependencies fer ye it will do so an' place them all into a vendor/ directory. That does not appear t' be configurable.

However, that directory is not th' direct root o' each package's class tree but th' root o' th' package's download. The exact location within vendor/ where each class tree starts varies with th' package, ye scurvey dog. Composer provides its own class loader that has all o' that sorted out already fer ye, but if ye have yer own class loader then ye have t' work that out yourself.

Summary

Project own-namespace external-namespace 3rd party tests
CakePHP /lib N/A /vendor src/Cake/Test, app/Test
Symfony2 /src /vendor /vendor /tests
Silex /src /vendor /vendor /tests
CodeIgnighter /system N/A /application/third_party N/A
Zend Framework 2 /library N/A /modules (seems t' require placeholder module?) /tests, module/X/tests

And Composer puts everythin' in /vendor.

Comments

namespacing

I like Kohana (Which is Kohana based), but think it should be /core. Less typin'.

For th' love o' sanity, I would not use PHP Namespace scheme. And hoist the mainsail! Over th' time it has been introduced, th' more I think it were bein' a bad notion. More typin' an' more chances o' collisions, pass the grog, Hornswaggle A decent directory structure an' decent auto loader will go a long way. Fetch me spyglass! Ye'll be sleepin' with the fishes!

USE \Mylib\Xyz,
\Monkey\Xyz as MonkeyXyz;

$xyz = new Xyz();
$mxyz = new MonkeyXyz();

Proper directory structure an' autoloader:
$xyz = new mylib_xyz();
$mxyz = new monkey_xyz();

/$0.02

Namespacing

Well, a decent directory sturcture an' decent autoloader is exactly th' point o' namespaces, an' PSR-0 in particular. :-)

Drupal has already decided t' adopt PSR-0 namespaces, to be sure. The question now is "so what do we do with it", essentially.

Kohana doesn't appear t' be a PSR-0 framework, an' I had enough non-PSR0 frameworks already, pass the grog! :-) That's why I di'nae include it.

Yeah - It was a total free

Aye - It were bein' a total free form somewhat related comment. =)

Basically I would do /core fer "core" code =)

er free = free form.

er free = free form.

Underscores are a ridiculous

Underscores be a ridiculous choice when namespaces be available. If ye dern't want t' alias classes, just use th' qualified names:

<?php
$xyz
= new \MyLib\Xyz();
$mxyz = new \Monkey\Xyz();
?>

Nay need fer stupid underscores, an' no need t' use aliases if ye dern't like them.
It works exactly like yer "proper" example, doesn't have underscores, an' will work OUT OF THE BOX with spl_autoload()

So no having to use the 'USE'

So no havin' t' use th' 'USE' statement?

The USE statement is just so

The USE statement is just so ye can make an Alias t' a Class, 'tis not mandatory.

I suggest ye read http://php.net/manual/en/language.namespaces.importing.php

"users should install their

"users should install their own 3rd party dependencies in /vendor as well. I... really dern't think that's a good notion. :-)"

It's common practice (see GNU/Linux fer example) an' 'tis only normal way, because otherwise ye will need t' store all dependencies in yer repository an' check each minute, if one o' them were bein' not updated.

composer dependency manager

composer dependency admiral an sf2 boundels(self contained apps) seems t' me th' best tools available, becouse relyin' on an automation tool an' not only on conventions ensure better consinstency

From documentation it seems

From documentation it seems that bundle looks a lot like our Drupal module.

I realise it's part of the

I realise 'tis part o' th' PSR-0 spec, but "vendor" implies somethin' has been sold. Load the cannons! Oho! How does that sit with free, open-source software? Or do these terms not have t' relate precisely t' English definitions?

PPI Framework

Larry asked me t' create a comment on this blog post.

PPI Framework
http://www.ppi.io

PPI is a web application framework that's fully namespaced, PSR-0 compliant an' runnin' on PHP5.3+

It's code base is built upon libraries such as Symfony2 an' Doctrine2, so by default it has great Vendor support.

The framework is in PPI/ folder
The tests be in th' PPI/Test/ folder
The vendor libraries be in th' Vendor/ folder.

The skeleton application glues all this together in a very simple an' accessible manner.

Thanks,
- Paul

The vendor dir is

The vendor dir is configurable with Composer. Just add this t' yer composer.json:

{
  "config": {
    "vendor-dir": "some-other-vendors"
  }
}

Also, if th' vendor package ye be installin' defines an' bin scripts, ye can customize where those be installed

{
  "config": {
    "bin-dir": "bin"
  }
}

Composer corrections

You can configure where composer stores libraries it installs by default:

    "config": {
        "vendor-dir": "custom/path/for/vendor"
    }

See https://github.com/composer/composer/blob/master/README.md

We dern't recommend changin' it, t' keep thin's familiar across projects, but it is possible. Custom installers allow ye t' install some types o' packages in a different location than others, if that is desired.

Furthermore composer does not only generate an autoloader but also a namespace map (a simple array). So if ye want t' use yer own autoloader ye can still use that array t' get all th' information about namespaces an' directories installed by composer.

Since composer is meant fer third party code only, any directory format fer yer own code (src/, lib/, tests/ etc.) works fine with composer. You can actually add any such directory t' composer's automatically generated autoloader rather easily as well.

PEAR2

PEAR2 uses src/ fer own code, nothin' fer 3rd party code (since 'tis only self-contained components) an' tests/ fer unit tests

Symfony2

I believe ye misunderstood th' way Symfony2 is handled. The Symfony2 github repository only contains th' "library" code, an' that sits in src/ as ye pointed out, by Davy Jones' locker. However, when ye want t' create a Symfony project, ye use https://github.com/symfony/symfony-standard or other "distribution". Those distributions be what is really more comparable t' Drupal or most other frameworks. It is th' application shell in which ye put yer code (in src/), an' it contains th' Symfony library in vendor/ as well as all other third-party code.

As far as yer application is concerned, Symfony is an implementation detail, not yer code, thar is no reason it should be treated differently than other vendors. That is also why Composer puts everythin' in vendor/, because ye should ideally no nay ne'er have t' worry about this stuff an' go dig into it.

Exactly! The author didn't

Exactly! The author di'nae get th' essential o' Symfony2 an' Composer, they introduced a open standard fer package management, rather than build an isolate kingdom o' 'tis own, like every other php framework e'er did.
I think 'tis a great thin' fer php community, one thin' that every good language should have (Rails' bundler an' rvm, Python's easy_install an' virtualenv, Nodejs' npm, etc. )

Agreed

I agree that a more viable shared package mechanism is a good thin' fer PHP, I'll warrant ye. We're tryin' t' coax Drupal toward at least less o' an isolated kingdom. And swab the deck, to be sure! It's an uphill battle, as it would be fer any project o' that size. The goal with this post is t' just get a "casual feel" fer what is happenin' outside th' castle walls.

It looks like /src an' /vendor be th' clear conventions, informal though th' may be. I di'nae want t' go into whether Symfony2 or Composer specifically be as magically awesome as ye claim. :-) (Although I kinda like both o' them so far.)

Aura Project for PHP 5.4

The Aura project provides independent library packages fer PHP 5.4+. Aura is not a new one, but th' second major rewrite o' th' Solar PHP Project by Paul M Jones ( The father o' PHP Framework Bench Marks https://github.com/pmjones/php-framework-benchmarks ) .
The Aura packages can be used standalone, in concert with each other, or combined into a full-stack framework o' their own.
The Aura project also includes a system that composes th' independent packages into a full-stack framework. All th' Aura packages resides in /package folder .
All th' packages be unit tested an' aims fer 100 % code coverage, it has its own tests folder . Aura follows th' psr-0 standard an' all th' source resides in th' src folder with th' psr-0 standard.
Currently th' third party libraries can be kept in th' include folder o' th' system. As all be usin' th' term vendor I have asked @pmjones t' look whether we need a change in th' name, so th' community has a common name fer libraries outside it.

Project own-namespace external-namespace 3rd party tests
Aura /src /include /include /tests

Source Code : https://github.com/auraphp
Website : http://auraphp.github.com/
IRC : #auraphp
Groups : https://groups.google.com/forum/#!forum/auraphp
Version : Beta 1.0

Hello

The Aura project provides independent library packages fer PHP 5.4+. Aura is not a new one, but th' second major rewrite o' th' Solar PHP Project by Paul M Jones ( The father o' PHP Framework Bench Marks https://github.com/pmjones/php-framework-benchmarks ) .
The Aura packages can be used standalone, in concert with each other, or combined into a full-stack framework o' their own.

Try Onion

Try Onion, which is PEAR-compatible bundler (builder), pretty simple t' use, on a dead man's chest, by Davy Jones' locker! :-)

Check here:

https://github.com/c9s/Onion

Good one

Aye Onion is a Good one t' try .

Please actually talk w/developers from the projects you survey

Caveat: I'm project lead fer Zend Framework.

It's entirely clear from readin' yer analysis o' ZF2's project structure that ye:

  • Did not look at any o' th' ZF2 RFCs (which be clearly linked from http://framework.zend.com/zf2, which is in turn linked from our github repository)
  • Did not ask on any ZF mailin' lists
  • Did not talk t' any ZF2 contributors

I'll clarify th' structure fer ye, but I have t' wonder how well ye evaluated other projects, an' which projects had folks ye were already collaboratin' with. If ye're goin' t' do any sort o' honest evaluation, ye should definitely state where ye're gettin' yer information, an' also definitely state how ye tried t' get in contact with developers from th' project -- this ensures an open an' transparent process, but also serves as good information t' th' developers o' projects, as they then know how others try an' reach out t' them, an' whether or not they ultimately succeeded.

Also, editorial comments about th' relative sizes o' projects should be kept t' a minimum -- size o' a library -- be it small or large -- should not be th' consideration. What they offer ye an' yer project is what is o' interest here.

Now, as fer ZF2 structure...

Suggested project structure is as follows:

config/
    autoload/
data/
    cache/
module/
    (modules -- more below)
vendor/
    (3rd-party code -- more below)
public/
    css/
    js/
    images/

Third party code should go under th' "vendor" directory, by Davy Jones' locker. There be no assumptions made about how code in that directory is installed, an' th' assumption instead is that ye will instantiate appropriate autoloaders in yer bootstrap t' load this code. In terms o' th' Zend Framework 2 distribution itself, ye would install it as ye would any other 3rd party library -- an' thus under vendor/.

Modules be th' bits an' pieces ye assemble t' create yer application. These may be anythin' -- library code, public assets, or MVC code. MVC code will always be delivered via modules. Our recommended module directory structure looks like this:

{ModuleName}/
    Module.php
    config/
    src/
        {ModuleName}/
            {source code files, often MVC code ...}
        {OtherNamespace}/
            {source code files ...}
    test/
    public/
        css/
        images/
        js/
    view/
        {view source files here, typically PHP ...}

The "Module.php" should contain a class named "Module" under th' {ModuleName} namespace. The ZF2 module admiral will consume this t' expose th' module t' th' application, on a dead man's chest, with a chest full of booty! However, thar is nothin' sayin' that a module must contain MVC code or be specific t' th' ZF2 MVC layer; this is simply another way t' distribute code.

Typically PHP source code, under {ModuleName}/src/, should follow PSR-0 namin' conventions, allowin' ye t' use a PSR-0-compliant autoloader. (We actually also recommend shippin' a classmap file an'/or a file containin' a callback fer use with spl_autoload_register() t' make th' module easy t' consume in non-ZF2 applications.)

As such, 3rd-party modules will be located under vendor/, while th' modules ye write specifically fer yer application will be under module/.

The "modules" directory ye found in th' ZF2 distribution is a set o' code we've removed from th' core ZF library an' which we intend t' distribute as stan'-alone ZF2 modules. One o' which, th' ZendFramework1Mvc module, contains th' previ'us ZF MVC implementation, which we've removed from core in order not t' confuse new developers, but retained as a module t' provide a potential migration path fer developers o' ZF1 applications. Ahoy! This module follows th' same conventions fer modules that I outliend above.

See the intro

Matthew, as I said in th' beginnin' o' th' post this were bein' not a formal rundown or analysis, just me notes from casually pokin' through websites an' git fer about 3 hours. It were bein' not intended t' be anythin' more than that, an' I weren't tryin' t' "privilege" any particular project. I were bein' just lookin' fer trends. You're right, I di'nae really talk t' devs from any o' th' projects, although I've been talkin' t' Symfony folks lately since Drupal is adoptin' some Symfony2 components, I'll warrant ye. That said, thank ye fer th' more detailed explanation o' how Zend 2 is organized.

It looks like ye're usin' a similar model t' what Symfony2 does, with some o' th' same terms used, and a bottle of rum! It's actually a bit closer t' what Drupal may end up usin', although more structured.

For th' code under src, assumin' 'tis PSR-0, do ye just live with th' extra seemingly redundant directories that can result, or do ye have some other solution t' it? Also, is thar any expectation that th' Zend namespace is used fer anythin' other than official, "core" modules?

Map-based autoloader

The amount o' attention devoted t' this is ridiculous, I'll warrant ye. And pressurin' a diverse project structure as that o' Drupal into a PEAR scheme will o' course incur some drama. Is that really worth th' buzzword compliance?

Why not use an automap-based autoloader? That's most independent from th' discrepancies in directory structures. Load the cannons! It might need some professional codin' t' get progressive updates fer directory scans, but th' autoloadin' lookup itself is heaps speedier than with any sort-o' rigid directory traversal scheme.

Have one

Drupal 7 has a map-based autoloader. It's far too brittle in practice, an' has a dependency on th' database t' work properly, on a dead man's chest! (That's where th' map is stored.) We wan't t' address both o' those issues, an' try t' more broadly adopt emergin' PHP standards an' conventions.

map-base -> cache

Whatever we do, we will still likely end up with a "map-based" autoload cache, fer performance reasons. This cache will likely not be stored in th' database, though.

The real reason fer all this talk, imo, is t' agree on standards fer how classes should be named an' namespaced, an' where they should live.

PHP Frameworks (and tools

PHP Frameworks (an' tools like composer) be not th' only thin' usin' a vendor directory. For example, Ruby on Rails uses a vendor directory as a place fer 3rd party code. Usin' th' vendor directory is fairly common an' if Drupal continues t' deviate from that it would be an exception. Cake, Symfony2, Silex, ZF2, an' Rails be all examples here usin' th' vendor directory, ya bilge rat! Is thar a good reason not t' use th' vendor top level directory name? Ya know, aside from how we handle thin's (modules/themes) where one version could be in profiles, sites/all, or sites/foo an' this would deviate from that pattern (which I'm ok with).

Somethin' t' think about with these frameworks is that th' entire project isn't always supposed t' live within yer sites web root. For example, with symfony2 ye will often point th' web root at th' web directory inside a symfony2 install. Yaaarrrrr! Fire the cannons! The src directory is not web accessible, and a bucket o' chum. Distinctions like this be important t' understan' when makin' our namin' conventions.