This article is part of a series on design patterns for framework independent code, especially to write extensions for Magento 1 and Magento 2 alike.
- Introduction: Write Extensions For Magento 1 And Magento 2
- Part 1: Accessing Configuration Data
- Part 2: Using Dependency Injection
- Part 3: Building Bridges
- Part 4: Preparing Data For Output
- Part 5: Using Advanced Autoloading
- Part 6: Leveraging PSR Interfaces
- Part 7: Iterating Iterators
PSRs (actually “PHP Standards Recommendations”) are defined by PHP-FIG, the PHP Framework Interop Group, a group of various PHP framework and library represantatives who strive for better interoperability. It started with the autoloading standard PSR-0 and the coding style standards PSR-1 and PSR-2. But now there are also some standard interfaces for common tasks. Let’s take a look at each to see how they can be of use to framework independent extensions.
Overview of PSRs:
- PSR-0: Autoloader (deprecated, replaced by PSR-4)
- PSR-1: Basic Coding Standard
- PSR-2: Coding Style Guide
- PSR-3: Logger Interface
- PSR-4: Autoloader
- PSR-6: Caching Interface
- PSR-7: HTTP Message Interface
Find the current list at http://www.php-fig.org/psr/. At the time of writing, PSR-5 and PSR-8 to PSR-13 are in “Draft” status.
PSR-3: Logger Interface
So if you want to log something, require and use the PSR LoggerInterface and most frameworks will be able to inject their logger instance. For other non-standard loggers it is easy to write an adapter. The PSR-3 repository comes with an abstract base class AbstractLogger that we can extend. An adapter for the Magento 1 logger can be written as follows:
final class IntegerNet_Solr_Model_Log extends AbstractLogger
* @var array
protected static $_levelMapping = array(
LogLevel::ALERT => Zend_Log::ALERT,
LogLevel::CRITICAL => Zend_Log::CRIT,
LogLevel::DEBUG => Zend_Log::DEBUG,
LogLevel::EMERGENCY => Zend_Log::EMERG,
LogLevel::ERROR => Zend_Log::ERR,
LogLevel::INFO => Zend_Log::INFO,
LogLevel::NOTICE => Zend_Log::NOTICE,
LogLevel::WARNING => Zend_Log::WARN,
* @var string
protected $_file = 'solr.log';
* @param string $file
* @return $this
public function setFile($file)
$this->_file = $file;
* Logs with an arbitrary level.
* @param mixed $level
* @param string $message
* @param array $context
* @return null
public function log($level, $message, array $context = array())
Mage::log($message, self::$_levelMapping[$level], $this->_file);
This model can be instantiated as singleton if it should always log to the same file, or you can create multiple instances for different log files. PSR-3 itself is unaware of log files.
PSR-6: Caching Interface
This PSR provides a set of interfaces for caching. It was heavily disputed, and many found the end result halfhearted – either too complex or not complex enough. Indeed, it looks quite complex for simple tasks. On the other hand advanced concepts like cache tags are missing.
I used the cache interface for some basic caching needs, but it felt like overcomplicating things, so in hindsight I’d rather define my own interface that only contains what I need and write adapters for the target systems.
As Anthony Ferrara sums it up:
“The beauty of OOP is that you can always build an adapter from a complex system to a simple interface. So standardizing on the simple system will allow for incredibly complex use cases to interact with each other, via simple and easy to understand interfaces.” — http://blog.ircmaxell.com/2014/10/an-open-letter-to-php-fig.html
If you plan to write implementations in a framework that actually uses PSR-6, your mileage may vary.
PSR-7: HTTP Message Interface
This PSR defines interfaces for HTTP requests and responses and clears the way for various middleware implementations. PSR-7 middleware can be used as an additional layer in front of a PSR-7 compatible application. A list of PSR-7 middleware can be found at https://github.com/oscarotero/psr7-middlewares – for example to add cache control headers or basic authentication.
If your extension operates directly on HTTP request and response, you might want to consider using PSR-7. This way you can write framework independent controllers or middleware.
Again you will need to write adapters for target systems that do not use PSR-7 (Magento 2 request and response classes for example are not PSR-7 compliant).
In most cases this is not worth the trouble. For example, a standard Magento controller will either a) do something based on the request, then redirect, or b) load data based on the request and render the page based on layout XML.
In case (a), it should be sufficient to pass the required request parameters to the library. The redirect can be implemented in the Magento module with one line of code.
In case (b), the layout (i.e. the response) is handled by Magento, and we can again delegate to the library from the controller and pass the needed parameters. See also Part 4: Preparing Data For Output.
- PSR-6 was finally approved in December 2015, so it’s still quite new. It will be more interesting as soon as more projects start to adopt it.
- Another PSR with standard interfaces is PSR-11: Container Interface, which is still in draft status. If it gets approved and used widely, we will have a standard interface to use the frameworks IoC container for dependency injection. An inofficial version is already published and being used by some projects as container-interop/container-interop
And now last but not least, the grand finale of this blog series: Iterating Iterators.
Author: Fabian Schmengler
Fabian Schmengler is Magento developer and trainer at integer_net. His focus lies in backend development, conceptual design and test automation.
Fabian was repeatedly selected as a Magento Master in 2017 and 2018 based on his engagements, active participation on StackExchange and contributions to the Magento 2 core.