An opinionated document of guidelines for sub-namspace conventions for SilverStripe projects and modules.
This is not intended to capture every possible situation, but provide a guideline for most common situations.
In the broadest definition namespaces are a way of encapsulating items. This can be seen as an abstract concept in many places.
For example, in any operating system directories serve to group related files, and act as a namespace for the files within them. As a concrete example, the file foo.txt can exist in both directory /home/greg and in /home/other, but two copies of foo.txt cannot co-exist in the same directory. In addition, to access the foo.txt file outside of the /home/greg directory, we must prepend the directory name to the file name using the directory separator to get /home/greg/foo.txt. This same principle extends to namespaces in the programming world.
In the PHP world, namespaces are designed to solve two problems that authors of libraries and applications encounter when creating re-usable code elements such as classes or functions:
- Name collisions between code you create, and internal PHP classes/ functions/constants or third-party classes/functions/constants.
- Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving readability of source code.
PHP Namespaces provide a way in which to group related classes, interfaces, functions and constants. Here is an example of namespace syntax in PHP:
namespace MyOrg\PackageName;
Much like directories and files, PHP namespaces also contain the ability to specify a hierarchy of namespace names. Thus, a namespace name can be defined with sub-levels:
namespace MyOrg\PackageName\Folder;
Code can be organised as deep in a hierarchy as needed.
namespace MyOrg\PackageName\Folder\SubFolder\ChildFolder;
The only requirement for the namespace hierarchy is the subdirectory name MUST match the case of the sub-namespace name.
SilverStripe introduced first-class support for namespaces in the 4.0 release [1]. Extensive work has gone into namespacing the SilverStripe Core Framework and supported modules. Third-party developers have also been encouraged to namespace their plugins for SilverStripe 4.0 support.
Some examples of namespaced classes in SilverStripe 4:
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\TextField;
At the current moment, there does not exist any conventions for organising sub-namespaces. Neither SilverStripe nor PHP-FIG define any standard sub-namespace folders or recommendations. This allows developers to make their own judgement for sub-namespaces. Over time, developers have settled into their own patterns of naming schemes including myself.
This document outlines my own reasoning and approach to organising code.
package └── src/ │ ├── Control/ │ │ └── Middleware/ │ ├── Forms/ │ ├── Exceptions/ │ ├── Extensions/ │ ├── Helpers/ │ ├── Interfaces/ │ ├── Jobs/ │ ├── Model/ │ ├── Reports/ │ ├── Services/ │ └── Tasks/ └── tests/
Not all of the above folders will be required for each module.
SilverStripe Core differs, silverstripe-framework
defines a Control
folder and silverstripe-cms
defines a Controllers
folder. For purposes of a plugin or application code, Control
is consistent with Model and reflects that this folder stores more than just Controller
subclasses but anything to do with the HTTP lifecycle.
use ExampleOrg\ExampleProject\Control\TestController;
use ExampleOrg\ExampleProject\Control\Middleware\TestMiddleware;
Controllers
- Used in core forsilverstripe-cms
howeverControl
makes more sense for things related to the HTTP lifecycle
This layout requires a
PageController
class to live in a namespace different to thePage
class. SilverStripe currently does not support this and a work around is needed. https://gist.github.com/wilr/5c74e1ba939ef240331052092c2889fa
Several modules solve this by using a PageTypes
folder.
SilverStripe Core and modules such as CMS
use the Forms
folder consistently.
use ExampleOrg\ExampleProject\Forms\BetterForm;
use ExampleOrg\ExampleProject\Forms\BetterTextField;
Any subclasses of Exception
.
Any subclasses of Extension
.
Several Core Developers prefer putting DataExtension
subclasses into Model
since they potentially alter the DataModel
but I've found it easier to identify any modified classes if the extensions which modify behaviour are keep seperate.
A helper class tends used internally to provide some work that has no business domain meaning. For example, ArrayHelper
might provide array utilities.
If required, group any interfaces in here rather than the Model
folder.
Any subclasses of QueuedJob
that run if present.
Any subclasses of DataObject
Any subclasses of Report
A Service class provides a way of a client to interact with some functionality in the application. This is typically public, with some business meaning. For example, a PaymentService
interface might allow you to pay
or refund
an object. Service
is common in modules such as QueuedJobs
.
Service/
- Used in several core modules, plural recommended.
Any subclasses of BuildTask
which are runnable by the developers.
Task/
- Plural recommended.
Please feel free to raise any Pull Requests to clarify or request more information. Tweet me at @wilr.