Introduction
If you have been watching the development of PHP over the last few years, you will know all about the problems with the language. The standard story is that PHP is a fragmented language, it is a language for hacks, there is no real specification, and so on and so forth. The reality is that PHP has grown up a lot recently. PHP 5.4 brings the language closer to a complete object model, and supplies a lot of new functionality.
So far, so good. But what about frameworks? There are a number of PHP frameworks available out there, and you only have to search the internet to realize you can spend your entire lifetime learning how they all work, because every day there is a new framework that does things in new ways. How do we reconcile all of these things into something that does not chase developers away from the language, while making it really simple to move functionality (and even entire modules) from one framework to another?
Enter PHP-FIG
PHP-FIG is the short name for the PHP Framework Interop Group (am I the only one who finds the naming of PHP groups and libraries after fruits amusing?) and their mission is simple: to find a way to get the PHP frameworks to work together.
Imagine this: you are currently working on a Zend Framework 2 project that needs a shopcart module. You built a shopcart module for your previous project, but this was a Symfony project. What can you do now but try to copy the code across and get things to work as best as possible? Well, since both Zend Framework 2 and Symfony are part of the PHP-FIG, you can simply import your shopcart module from one framework to another and use it. Sound good? I think it does, and while we are not yet at that point, the PHP-FIG is moving in that direction with haste.
Let’s have a look at who is part of the PHP-FIG and what they are doing.
Membership
Anyone can become a member of the PHP-FIG, as long as you have an active PHP framework. However, current members vote on admissions and if you don’t have a largish community for your framework, you probably won’t get in. This is to stop the one billion little framework developers having a say in things when they really don’t have any sway in the framework community.
Members currently include:
* Agavi
* AWS SDK for PHP (Amazon Web Services)
* Apache log4php
* Assetic and Buzz
* Aura Project and Solar Framework
* CakePHP
* Composer and Packagist
* Contao Open Source CMS
* Doctrine
* Drupal
* eZ Publish
* TYPO3 Flow
* Jackalope
* Joomla
* Laravel
* Lithium
* PEAR
* phpBB
* phpDocumentor
* PPI Framework
* Propel
* PyroCMS
* SabreDAV
* Sculpin
* SugarCRM
* Symfony2
* The Community At Large (Cal Evans)
* Yii framework
* Zend Framework 2
* Zikula
PSRs
A PSR is a PHP Standards Recommendation, and is the result of the work that the PHP-FIG is doing. Members of the group suggests rules for each PSR, and the rest of the group votes on whether these rules should be allowed or not. This discussion takes place in Google Groups, and the PSRs are available on the PHP-FIG website. Below, we’ll look into some of those PSRs.
PSR-0 – Autoloading Standard
The first step in getting all frameworks to work together is by having a common directory structure and therefore a common autoloading standard. In other words, all frameworks should be built using the same autoloading conventions.
Rules
1. A fully-qualified namespace and class must have the following structure ()*
2. Each namespace must have a top-level namespace (“Vendor Name”).
3. Each namespace can have as many sub-namespaces as it wishes.
4. Each namespace separator is converted to a DIRECTORY_SEPARATOR when loading from the file system.
5. Each _ character in the CLASS NAME is converted to a DIRECTORY_SEPARATOR. The _ character has no special meaning in the namespace.
6. The fully-qualified namespace and class is suffixed with .php when loading from the file system.
7. Alphabetic characters in vendor names, namespaces, and class names may be of any combination of lower case and upper case.
An example autoloading function is supplied, and looks like this:
<?php
function autoload($className)
{
$className = ltrim($className, '');
$fileName = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
PSR-1 – Basic Coding Standard
This PSR deals with the basic coding standards of PHP, and the idea is that if all developers use the same standard, then it will be easy to move code around without having any problems.
Rules
1. Files MUST use only <?php and <?= tags
2. Files MUST use only UTF-8 without BOM for PHP code
3. Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but should not do both
4. Namespaces and classes MUST follow PSR-0
5. Class names MUST be declared in StudlyCaps
6. Class constants MUST be declared in all upper case with underscore separators
7. Method names MUST be declared in camelCase
PSR-2 – Coding Style Guide
This PSR extends PSR-1, and supplies a set of rules on how to format PHP code.
Rules
1. Code MUST follow PSR-1.
2. Code MUST use 4 spaces for indenting, not tabs.
3. There MUST NOT be a hard limit on line length; the soft limit MUST be 120 characters; lines SHOULD be 80 characters or less.
4. There MUST be one blank line after the namespace declaration, and there MUST be one blank line after the block of use declarations.
5. Opening braces for classes MUST go on the next line, and closing braces MUST go on the next line after the body.
6. Opening braces for methods MUST go on the next line, and closing braces MUST go on the next line after the body.
7. Visibility MUST be declared on all properties and methods; abstract and final MUST be declared before the visibility; static MUST be declared after the visibility.
8. Control structure keywords MUST have one space after them; method and function calls MUST NOT.
9. Opening braces for control structures MUST go on the same line, and closing braces MUST go on the next line after the body.
10. Opening parentheses for control structures MUST NOT have a space after them, and closing parentheses for control structures MUST NOT have a space before.
PSR-3 – Logger Interface
The PSR-3 deals with logging, and specifically exposes nine methods that can write logs for the framework libraries. This makes logging universal for all frameworks, meaning that when you want to implement logging in a framework that does not already have logging, the process is trivial.
The Methods
1. The LoggerInterface exposes eight methods to write logs to the eight RFC 5424 levels (debug, info, notice, warning, error, critical, alert, emergency).
2. A ninth method, log, accepts a log level as first argument. Calling this method with one of the log level constants MUST have the same result as calling the level-specific method. Calling this method with a level not defined by this specification MUST throw a PsrLogInvalidArgumentException if the implementation does not know about the level. Users SHOULD NOT use a custom level without knowing for sure the current implementation supports it.
PSR-4 – Improved Autoloading
As an extention to PSR-0, PSR-4 supplies an improved method of autoloading, and also describes the file path requirements of an interoperable autoloader.
The Rules
1. The term “class” refers to classes, interfaces, traits, and other similar structures
2. A fully qualified class name has the following form: <NamespaceName>(<SubNamespaceNames>)*<ClassName>
1. The fully qualified class name MUST have a top-level namespace name, also known as a “vendor namespace”.
2. The fully qualified class name MAY have one or more sub-namespace names.
3. The fully qualified class name MUST have a terminating class name.
4. Underscores have no special meaning in any portion of the fully qualified class name.
5. Alphabetic characters in the fully qualified class name MAY be any combination of lower case and upper case.
6. All class names MUST be referenced in a case-sensitive fashion.
3. When loading a file that corresponds to a fully qualified class name …
1. A contiguous series of one or more leading namespace and sub-namespace names, not including the leading namespace separator, in the fully qualified class name (a “namespace prefix”) corresponds to at least one “base directory”.
2. The contiguous sub-namespace names after the “namespace prefix” correspond to a subdirectory within a “base directory”, in which the namespace separators represent directory separators. The subdirectory name MUST match the case of the sub-namespace names.
3. The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
4. Autoloader implementations MUST NOT throw exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value.
Conclusion
PHP-FIG is changing the way we write frameworks, but not how they work. This is good for all of us. Clients often require you to work on existing code that is built in a framework, or specify which framework you should use for their project. The PSRs will make that a lot simpler for us developers in the future, and I think this is very important. The word in the PHP community is that the PSRs will eventually be built into the PHP core. This might be a good thing in the future, although I’m not sure how PSR-1 and PSR-2 would be built in at this point.
You can keep an eye on the PHP-FIG by subscribing to their mailing list, or joining them on freenode. Note, I am not a member of PHP-FIG.