Common convention for namespaces in PHP is to start with
Vendor\Package, capitalized (
CamelCase ) with “vendor” and “package” analogous to the composer package name.
There is a bad habit I see often, probably coming from the ZF1 and Pear days, where every word in the class name is a new sub namespace (and a new subdirectory), or child classes are moved into a namespace with the name of the parent class. All this is leading to deeply nested namespaces and class names that have no meaning without their namespace.
Examples from Zend Framework 1 (pseudo namespaces):
Zend_Db_Table_Row_Abstractan abstract base class for
Zend_Db_Table_Row, representing a database table row. There are also
Zend_Pdf_FileParser_Font_OpenType_TrueTypea parser for true type font files. The class extends
And a current example from Magento 2:
Magento\Catalog\Model\Product\Option\Type\File\Validator– A validator for product options of the type “file”
This makes a very confusing directory structure, deeply nested, and with classes that belong together not necessarily next to each other.
├── Option │ ├── Converter.php │ ├── ReadHandler.php │ ├── Repository.php │ ├── SaveHandler.php │ ├── Type │ │ ├── Date.php │ │ ├── DefaultType.php │ │ ├── Factory.php │ │ ├── File │ │ │ ├── ValidateFactory.php │ │ │ ├── ValidatorFile.php │ │ │ ├── ValidatorInfo.php │ │ │ └── Validator.php │ │ ├── File.php │ │ ├── Select.php │ │ └── Text.php │ ├── Type.php │ ├── UrlBuilder.php │ ├── Validator │ │ ├── DefaultValidator.php │ │ ├── File.php │ │ ├── Pool.php │ │ ├── Select.php │ │ └── Text.php │ └── Value.php ├── Option.php
The other extreme is no sub namespaces at all, leaving all class files in one directory (or even all classes in one file)
This is perfectly fine for a small package with about a dozen classes, but not if it gets bigger.
Since I prefer having many small classes over a few big ones, I have to find a middle ground. Otherwise the code base will look complex at first glance where it actually is not, just because the parts are shuffled without sensible arrangement.
But what is wrong with the examples above?
- Unnecessary deep nesting Take the Zend_Db classes: Databases, tables, rows and columns (and more) make up a database schema and are tightly coupled with each other. You won’t have tables without rows and rows without tables. So if all these classes logically belong together, why should I look for them in different directories? If I browse the code in the IDE or on Github I want to be able to navigate quickly between classes that belong together. And if it is unfamiliar code, I want to quickly grasp which classes belong together. This is easier if they are in the same place, for example like this:
RelationalDbSchema ├── Database.php ├── Table.php ├── Row.php ├── Column.php
With the PDF file parser classes it is similar, although they have another problem: overuse of inheritance. That aside, a better structure could look like this:
FileParser ├── BaseFileParser.php ├── FontFileParser.php ├── OpenTypeFontFileParser.php ├── TrueTypeFontFileParser.php
- Hard to find classes Most IDEs have a feature to search for classes by name, for example for code completion. Now if you have 50 classes called “File” or “Validation” in different namespaces used for entirely different things, you are making it hard to find the right one. However, if there is one single “FileOptionValidation” class, I can find and use it with just a few keystrokes.
So here are some rules I came up with that work well for me so far:
Keep classes organized
- Keep namespace depth as flat as possible. This makes it easier to navigate the code.
- Start with zero, i.e.
Vendor\Package\Class, and only introduce sub namespaces if there is need for it (see below).
- If the number of classes in a namespace is too big to find your way in it, think(!) about breaking it apart 1 10 or even 50 classes or interfaces in a namespace are still okay, if they really belong together
- Think of each sub namespace as a separate component and keep coupling between the components small. This will help you organize the namespaces in a sensible way.
- The class name should always be descriptive enough to work without the namespace (i.e.
Product\Repository). This will help to quickly find the right classes. Also you can let your IDE import them automatically and you won’t need to define aliases all the time.
- Avoid namespaces that contain both, classes and sub-namespaces. This is the least important rule, but I think it can be a useful guideline to keep the distance between classes that belong together low. An exception can be a class that serves as single entry point for the whole package that has sub-namespaces. This will reside as only class at the top level next to the sub-namespaces to make it obvious that this is where you start using the package.
Reddit user LtAramaki makes an interesting point:
I can see you seem to equate “namespace” and “package”, but they really aren’t the same thing
I have thought about it and could not find arguments against a class like
Vendor\Packageas single entry point for the package. I will consider this in the future.
I mostly stick to the “one class per file” convention and PSR-4, i.e. namespaces mapped to directories. The reasons are practical: autoloader compatibility and the “principle of least astonishment”.
“Private classes” inside another class file are a rare exception and only if they are not used anywhere outside that “parent” class.
How do you organize your namespaces?
- In the first version of this article I wrote “If the number of classes in a namespace reaches 5, think(!) about breaking it apart“. But giving hard numbers like that might result in misconceptions. For small packages I found it a good rule of thumb, but following it literally in a larger package will lead to exactly the namespace cluttering I want to avoid. ↩
5 Replies to “Sensible Namespaces in PHP”
Nice post and I totally agree.
Furthermore I like classnames having a postfix indicating their type.
Like in the piece of code you are showing above, you have a Validator directory presumably containing different validators (File, Text, Select). Supposedly those are all validators, I would prefer there names to be FileValidator, TextValidator, SelectValidator.
That’s what I meant: without the namespace the class names do not tell us what they are. Not to be confused with redundant postfixes like “Interface” or “Abstract”.
I agree with Matthias. A really good blog post. Do we need a convention like Java?
A quick clarification that camel case actually starts with a lowercase (camelCase). Class names as defined by PSR-1 are to be StudlyCaps – https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md#3-namespace-and-class-names.
I loved all of the examples of overly complicated or nested namespaces. I don’t like apps that I have to go through multiple folders to get to a single class. Luckily GitHub direct links if directories are empty save for a child file.
Your comment on ProductRepository vs Product\Repository is good, but I think it depends on the use case and size of the app. On small, simple apps, I will add the type suffix (ie: Repository). On larger apps, I opt for organization without suffixes. With medium sized apps, I’ll simply group all of my repositories so I’d end up with Repositories\Product. Or in larger apps I opt for a more DDD approach and have all things “Product” grouped and end up with Product\Repository.
So, yeah, there is more than one way to skin a cat, and I think it just depends on the needs and size of the project. But you’re spot on with the “wrong” ways. I prefer to follow the standards of autoloading instead of underscores, and I think as a community we’re better for having those.
Thanks for the post!
I’m ceating my own framework. (Yes, im one of `those` guys…)
– A namespace is mapped to a file. Thus more than one class in one file.
– Namespace is ‘prefixed’ with it’s type, like: api/vendor/package/MyInterfaceDefinition
Thus i can have files that look like this:
vendor/package/myImplementiation.src.php <== 'default' if no prefix is used.
I also can have a special prefix: build/…/…
That just means: "hey, autoloader. If you can't find it, please let me know. I wil build/create it for you"
Names like: "default, class, object, string, exc, api, model"… can't be vendors, because of prefixes and invalid namespace names.
Advantage: I don't need a slow and complex autoloader. It's just a simple mapping that is a 'constant'. I like "constants"….My multi-core computer likes them also 🙂
Comments are closed.