DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • PHP vs React
  • Smart Dependency Injection With Spring: Assignability (Part 2 of 3)
  • Why I Started Using Dependency Injection in Python
  • Inheritance in PHP: A Simple Guide With Examples

Trending

  • Building Reliable LLM-Powered Microservices With Kubernetes on AWS
  • Blue Skies Ahead: An AI Case Study on LLM Use for a Graph Theory Related Application
  • AI-Based Threat Detection in Cloud Security
  • How Kubernetes Cluster Sizing Affects Performance and Cost Efficiency in Cloud Deployments
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Patterns: Plugin

Practical PHP Patterns: Plugin

By 
Giorgio Sironi user avatar
Giorgio Sironi
·
Oct. 11, 10 · Interview
Likes (0)
Comment
Save
Tweet
Share
4.9K Views

Join the DZone community and get the full member experience.

Join For Free

The Separated Interface pattern can often be used to provide hook points to client code, in the form of interfaces to implement or classes to extend with client code.

The right implementation to use in a part of the system can then be chosen via configuration: the Factory or Dependency Injection container with the largest scope would process the configuration and execute conditionals only one time, and inject the right Plugin as a collaborator of a standard object.

This pattern is a evolution of the Separated Interface one, where the implementor package is not even under your maintenance, but it is provided by some external developer that links his code to your work.

Implementation

In PHP the concept of compile time does not exist, apart from the just-in-time cached compilation of the scripts to operation codes, a phrase which you can peacefully ignore if you are not into caching. By the way, even if some checks are performed while loading and parsing the PHP code, PHP is by design a dynamic language where you can write nearly everything and it will not explode until executed.

This design leaves open many possibilities for inserting plugins, but due to the lack of compile there is often a lack of a clean separation between code and configuration. For example, database credentials are embedded in PHP code more often than in other languages.

Think now of a framework or a library: you cannot change the code but you must adapt or create a configuration to make it work. To implement a Plugin pattern, your application should strive towards the flexibility of a library: think of your production code as external and untouchable, and try to deploy a particular configuration to make it work and to modify a functionality. For example, extract it in a temporary working copy with svn checkout or git clone and hook in the necessary extensions.

When you succeed, and your svn diff or git diff is clean, you'll have implemented a Plugin system. Modification of vendor code (and you are the vendor here) is out of the question.

Future changes

Kent Beck says in Implementation Patterns that providing hooks via implementation and inheritance is one of the most effective ways to tie a framework down from future evolution.

For example, once you have published an interface, you cannot add methods to it without breaking all the implementors. You can publish versioned interfaces, but this adds complexity to your application.

With a published abstract class instead, you can include a default implementation for new methods, but you can't remove methods or refactor protected members without breaking Plugin implementators. This is the specular situation of providing an interface.

Zend Framework includes both an interface and an abstract class for most of its components, but it does not get right the management of extension points (at least in the 1.x branch). When including the possibility of Plugins in your application, default as much as possible to private visibility and hide the internals of your Plugin hook point. What is left to protected is a seam that screams "extend me", and the interfaces not marked as internal will be implemented by someone else. There is no built-in language mechanism to protect interfaces,m so you'll have to rely on some kind of convention (like a particular prefix or namespace), but for private methods left to protected scope we can only blame ourselves.

Configuration

The configuration of your Plugin system can be managed with solution of different levels of complexity, each more powerful than the previous ones. Of course, you shouldn't provide a needlessly complex system when all you need is a class name.

The first solution is indeed to insert class names into configuration files. This is a totally declarative approach, which uses simple INI files. This is commonly done in Zend Framework, for example with bootstrap resources, and in some cases can even manage dependencies of the Plugins. Bootstrap resources can request other object of the same kind, but cannot pull in arbitrary collaborators (unless they create them by themselves... ugly if you know what DI is).

A second, widely applicable solution is to request Factory objects. this solution still involves writing PHP code, but it is one step towards textual configuration. However, a Factory object can fetch and inject all the dependencies into a Plugin without cluttering it with this kind glue code (only a constructor or some setters).

The problem with Factories is that they tend to contain all the same boilerplate code. A third solution can be used to provide quick construction of objects: Dependency Injection containers, which have recently been introduced even in PHP. A DI container is configured textually, via an XML or INI file containing parameters like the collaborators each object requires, its lifetime, and so on. DI containers are probably the future of flexible PHP applications, but beware of growing too dependent on them: they are a library like every other open source component, and should be isolated from your code as much as possible like you would do with your models and Doctrine 2, or your services and Zend Framework.

Example

The code sample shows hot to predispose a class for receiving an injected simple Factory that manages user-defined plugins.

// plugin_view.php
<?php
echo "Today is ", $this->formatDate(time()), ".\n";
<?php
/**
 * The interface that user extensions should implement.
 * This Factory manages View Helpers as simple callbacks that once returned 
 * can be directly invoked.
 */
interface ViewHelperFactory
{
    /**
     * @return callback
     */
    public function getHelper($name);
}

/**
 * Our library code. This class renders an external script providing him
 * with $this for View Helper access. This is a technique widely used in
 * Zend Framework.
 */
class View
{
    /**
     * The hook: injecting a Factory here can change the behavior of the 
     * View object.
     */
    public function __construct(ViewHelperFactory $factory)
    {
        $this->factory = $factory;
    }

    public function render($script)
    {
        include $script;
    }

    /**
     * Forwards the call to the View Helper invoked.
     */
    public function __call($name, $args)
    {
        $callback = $this->factory->getHelper($name);
        return call_user_func_array($callback, $args);
    }
}

/**
 * Extension code.
 */
class UserDefinedFactory implements ViewHelperFactory
{
    private $helpers;

    /**
     * In this example, we only define a simple Plugin for
     * formatting dates using PHP's internal function.
     */
    public function __construct()
    {
        $this->helpers = array(
            'formatDate' => function($time) {
                return date('Y-m-d', $time);
            }
        );
    }

    public function getHelper($name)
    {
        return $this->helpers[$name];
    }
}

// client code
$view = new View(new UserDefinedFactory);
$view->render('plugin_view.php');
PHP Dependency injection Interface (computing)

Opinions expressed by DZone contributors are their own.

Related

  • PHP vs React
  • Smart Dependency Injection With Spring: Assignability (Part 2 of 3)
  • Why I Started Using Dependency Injection in Python
  • Inheritance in PHP: A Simple Guide With Examples

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!