Skip to main content

PHP plugin architecture

https://stackoverflow.com/questions/10053479/a-php-plugin-architecture

https://roots.io/routing-wp-requests/
https://stackoverflow.com/questions/42/best-way-to-allow-plugins-for-a-php-application

Foundation for create Object Oriented Framework
http://agile-core.readthedocs.io/en/develop/hook.html
http://agile-core.readthedocs.io/en/develop/index.html


UPDATE: answer now reflects the up-to-date links and better description.
There are certainly many different ways to design a plugin system and perhaps asking on https://softwareengineering.stackexchange.com/ would give you more ideas, but I'll try to help by sharing my ideas and experience.
I'll share some of my own experiences which I've learned through a series of my own frameworks. Currently Agile UI and Agile Data both support many wasy to be extended, but I'll focus on the "Components"

HOOKS

When you look to inject code into the existing object, a hook is a standard way to go. It's the best option for extending the application or flow with an established structure.
While refactoring my frameworks, I've separated hook implementation into a separate trait and documented it here: http://agile-core.readthedocs.io/en/develop/hook.html
Host application:
... some code here ..
$this->hook('beforeInit');
$this->init();
$this->hook('afterInit');
... code goes on ..
Plugin:
$host_app->addHook('beforeInit', function($object) {
    echo "About to execute init of $object";
});

UI COMPONENTS

Components present a different design pattern, which is suitable for the User Interfaces. You start with the page/app layout which is then broken down into Menu, Header, Footer, Content.
A component is an object which can be associated with Layout or the other component. Each component is capable of rendering and passing extra HTML/JS to its parent. Most of components would also be an interractive objects.
This approach is called "Render Tree" and application execution goes through 2 stages - "initialization of a render tree" and then "rendering".
Host Application:
$layout->menu = new \atk4\ui\Menu();
$layout->add($layout->menu, 'TopMenu');
The above code show how a new Component (Menu) can be initialized and inserted into the $layou. Furthermore the HTML output of $menu is directed into {$TopMenu} tag, which is defined in the HTML template of Layout.
Plug-in can interact with the render tree by:
  • adding more components anywhere in the tree
  • affecting existing components (e.g. add new menu item)
  • destroying any of the existing components
When those approaches are combined, you can use something like this:
$app->addHook('afterInitLayout', function($app) {

    $app->layout->menu->destroy(); // remove default menu
    $app->layout->menu = new \plugin\SuperMenu();
    $app->layout->add($app->layout->menu);
});
This can be used to replace standard menu with a more powerful implementation from your add-on.
My implementation of components is documented here:

UI / DATA SEPARATION

Although perhaps not so much answer for a question, but another powerful way to extending is a separation of concerns. All of the UI components in Agile UI do not know how to do anything with data.
While many UI generators require developer to manually build them up and link up with data, I am injecting "Model" object like this:
$form->setModel(new User($db)); // populates name, surname and gender fields
In this approach object User contains enough meta-data for the Form to populate it's fields, captions perform validation and also save/load data.
Since "User" class can also be declared in an add-on it makes a pretty powerful way to extend existing functionality by adding support for new data entities.

OTHER APPROACHES

Some other approaches to extend with add-ons include:
Factory:
The ability to resolve class specified as a string into namespace / file:
$api->add('MyClass');
Gives ability for add-on to re-route standard classes but is not very friendly with type-hinting in IDEs.
New types / Traits:
Add-on can provide new classes adding Persistences, Table Columns, Form Fields, Actions etc.

CONCLUSION

I think add-on deign boils down to:
  • simplicity of installation and use
  • does add-on need to operate out-of-the-box?
  • avoiding conflicts between add-ons
  • defining different add-on types - authentication, UI, behaviour
  • can add-on extend another add-on
  • will add-on fit application design, data and architecture?
  • give a lot of power to add-ons without sacrificing performance

You could use an Observer pattern. A simple functional way to accomplish this:
<?php

/** Plugin system **/

$listeners = array();

/* Create an entry point for plugins */
function hook() {
    global $listeners;

    $num_args = func_num_args();
    $args = func_get_args();

    if($num_args < 2)
        trigger_error("Insufficient arguments", E_USER_ERROR);

    // Hook name should always be first argument
    $hook_name = array_shift($args);

    if(!isset($listeners[$hook_name]))
        return; // No plugins have registered this hook

    foreach($listeners[$hook_name] as $func) {
        $args = $func($args); 
    }
    return $args;
}

/* Attach a function to a hook */
function add_listener($hook, $function_name) {
    global $listeners;
    $listeners[$hook][] = $function_name;
}

/////////////////////////

/** Sample Plugin **/
add_listener('a_b', 'my_plugin_func1');
add_listener('str', 'my_plugin_func2');

function my_plugin_func1($args) {
    return array(4, 5);
}

function my_plugin_func2($args) {
    return str_replace('sample', 'CRAZY', $args[0]);
}

/////////////////////////

/** Sample Application **/

$a = 1;
$b = 2;

list($a, $b) = hook('a_b', $a, $b);

$str  = "This is my sample application\n";
$str .= "$a + $b = ".($a+$b)."\n";
$str .= "$a * $b = ".($a*$b)."\n";

$str = hook('str', $str);
echo $str;
?>
Output:
This is my CRAZY application
4 + 5 = 9
4 * 5 = 20
Notes:
For this example source code, you must declare all your plugins before the actual source code that you want to be extendable. I've included an example of how to handle single or multiple values being passed to the plugin. The hardest part of this is writing the actual documentation which lists what arguments get passed to each hook.
This is just one method of accomplishing a plugin system in PHP. There are better alternatives, I suggest you check out the WordPress Documentation for more information.
Sorry, it appears underscore characters are replaced by HTML entities by Markdown? I can re-post this code when this bug gets fixed.
Edit: Nevermind, it only appears that way when you are editing

https://torquemag.io/2017/09/embracing-the-microservice-architecture-pattern-with-wordpress/

https://stackoverflow.com/questions/3356376/php-creating-extensible-cms-system/3356922#3356922

https://martinfowler.com/articles/201701-event-driven.html

https://martinfowler.com/eaaCatalog/plugin.html


Comments

Popular posts from this blog

Rand mm 10

https://stackoverflow.com/questions/2447791/define-vs-const Oh const vs define, many time I got unexpected interview question. As this one, I do not know much or try to study this. My work flow, and I believe of many programmer is that search topic only when we have task or job to tackle. We ignore many 'basic', 'fundamental' documents, RTFM is boring. So I think it is a trade off between the two way of study language. And I think there are a bridge or balanced way to extract both advantage of two method. There are some huge issue with programmer like me that prevent we master some technique that take only little time if doing properly. For example, some Red Hat certificate program, lesson, course that I have learned during Collage gave our exceptional useful when it cover almost all topic while working with Linux. I remember it called something like RHEL (RedHat Enterprise Linux) Certificate... I think there are many tons of documents, guide n books about Linux bu

Martin Fowler - Software Architecture - Making Architecture matter

  https://martinfowler.com/architecture/ One can appreciate the point of this presentation when one's sense of code smell is trained, functional and utilized. Those controlling the budget as well as developer leads should understand the design stamina hypothesis, so that the appropriate focus and priority is given to internal quality - otherwise pay a high price soon. Andrew Farrell 8 months ago I love that he was able to give an important lesson on the “How?” of software architecture at the very end: delegate decisions to those with the time to focus on them. Very nice and straight-forward talk about the value of software architecture For me, architecture is the distribution of complexity in a system. And also, how subsystems communicate with each other. A battle between craftmanship and the economics and economics always win... https://hackernoon.com/applying-clean-architecture-on-web-application-with-modular-pattern-7b11f1b89011 1. Independent of Frameworks 2. Testable 3. Indepe