Plugin System

41 views
Skip to first unread message

Jamie Rumbelow

unread,
Mar 26, 2009, 2:50:27 PM3/26/09
to recess-f...@googlegroups.com

Okay, since we didn’t get much discussion about the plugin system on our conversation last week, I thought it’d be best to put a topic up here so we can share ideas and get talking about how it’s going to work.

                                                                                                                                           

As far as Kev’s plugin, I like the idea, but I don’t think that we should take the ‘app in an app’ route. I just think that embedding controllers, models and views into a plugin causes a lot of problems, and the advantages aren’t that better than using a more conventional system. For one thing, a developer may want to use a different controller for handling sessions, and this would require the developer going into the plugin itself to modify the files – which defeats the purpose of a plugin.

 

A plugin should be:

 

·         An extension of functionality – it should provide a way of doing something either not possible with current tools or it should simplify the process

·         Abstract – a plugin can’t depend on anything application specific ie. Controller names, Routing structure

·         Independent – a plugin should not be aware of any other plugins in the system – it should not rely on any other plugins or have any internal dependencies

·         Structured – a set of conventions will be put in place (including, but not limited to file/folder names and method names/parameters) and these conventions should be followed as much as possible.

·         Reliable – a testing framework will be put in place for the plugin API, and plugin developers will be encouraged to test the plugin wherever possible.

 

Likewise, the API should be:

 

·         Conventional – any conventions already in the framework should remain for continuity

·         Modular – the API should be able to dissipate common areas of framework functionality (Request, I/O, DBA etc.) into classes and interfaces for the developer to extend, allowing the framework’s functionality to change.

·         Conscious – although extendibility is encouraged, the API should protect the core of the framework to make it as safe as possible and not let plugins interfere with the major framework functions. An example of this is to prevent a plugin from changing the loading mechanism.

 

I believe that a plugin’s file structure should look something like this:

 

/plugins –

 

                /plugin1 –

                               
                                _init.php
                                _close.php
                                _meta.php
                                /lib –

                                                Plugin1.class.php
                                                another_file.php

                                /recess –

                                                Plugin1.Annotations.class.php

                                                Plugin1.Model.class.php

                                                Plugin1.Controller.class.php

 

                /anotherplugin –

                               
                                _meta.php
                                /lib –

                                                AnotherPlugin.class.php

                                /recess –

                                                AnotherPlugin.Annotations.class.php

                                                AnotherPlugin.SomeFrameworkClass.class.php

 

A few notes:

 

·         _init.php gets called before any other file in the plugin. It allows the plugin to initialize any constants, variables or anything else it may need to operate.

·         ­_close.php gets called after the plugin has been fully loaded and the response has been sent to the browser, just before the core system shuts down. It should remain in the same scope as _init.php­ so it can reverse the changes made.

·         _meta.php contains any meta information about the plugin, including author, URL, description, licenses/copyright etc.

·         The plugin’s core class will be held in PluginName.class.php in the /lib directory. This will be the only class that gets loaded directly, and will therefore need to present an interface to the user’s app, which can then interact with other classes/files.

·         The /recess directory holds any system extensions/additions. The convention for files is PluginName.ClassName.class.php.

·         Not all files are required – only the _meta.php and lib/PluginName.class.php files are needed, this will make creating plugins easier and cleaner.

 

I’m sorry that’s a lot to digest, and not particularly organised, as I’ve simply spewed my ideas on the email and hoped it is coherent, but it should give us a lot to discuss and should present some interesting ideas.

 

Catch you later,

 

Jamie

 

Jamie Rumbelow Designer / Developer / Writer / Speaker

http://www.jamierumbelow.net | +44 (0)7956 363875 | jamie (at) jamierumbelow (dot) net

 

Joshua Paine

unread,
Mar 26, 2009, 3:18:38 PM3/26/09
to recess-f...@googlegroups.com
On Thu, 2009-03-26 at 18:50 +0000, Jamie Rumbelow wrote:

> · Independent – a plugin should not be aware of any other


> plugins in the system – it should not rely on any other plugins or
> have any internal dependencies

Only had time to skim this msg atm, but as discussed in IRC, a
Requires/Provides notation for plugins seems very acceptable to me. The
framework will keep track of whether dependencies are met and die
appropriately if they're not. Plugins are as likely to need other code
as anything else is.

(And I promise it's not just that I like disagreeing with you, Jamie.)

--
Joshua Paine
LetterBlock: Web applications built with joy
http://letterblock.com/
301-576-1920

Kris Jordan

unread,
Mar 26, 2009, 6:26:27 PM3/26/09
to recess-f...@googlegroups.com
Jamie -

These are great suggestions, thanks for putting the time in to writing them up. Before responding in depth to individual suggestions I want to take a moment to outline some core principles in Recess that should probably be outlined in a page on the website :) These principles form some constraints on plugins and should help frame the discussion.

* Principle #1: Recess Will Emphasize Object-Oriented PHP Design
Constraints Imposed: There are only 2 places where flat PHP files (not containing classes) are pardoned: in the bootstrapping process prior to Recess::main, and in view template files. Every other file Recess depends on should be solely classes or interfaces. 

* Principle #2: Recess' Low-level Components Will be Leveraged Where Possible
Constraints Imposed: Library is a critical system-level utility that Recess uses for class loading and is well optimized. The conventions Library uses for class loading will apply to the plugins directory the same as it does the apps directory (and recess directory). Library is responsible for many of Recess' key performance traits.


* Principle #3: Recess Will Minimize Concept Counts Wherever Possible
The constraints this principle suggests are less technical and more philosophical. There are two reasons for this: 1) More concepts increases the learning difficulty for developers. 2) More concepts leads to more code.

Given these three principles (there are more, but I think these are the key ones for the plugins discussion) I think some of your suggestions fit nicely while others are outside fall outside of our design constraints.

===

Re: "I don’t think that we should take the ‘app in an app’ route."

(Note: Kev is extending from Application currently, because it's the only way his Auth 'plugin' runs. Worth noting though that SimpleAuth actually does run if placed within the apps folder and configured correctly.)

There are many things in common between an application and a plug-in: models and controllers. Potentially annotations (I can imagine application specific annotations that make sense only within a single application and wouldn't be plug-ins.) Potentially default views (which, if in a plugin, could be overridden in applications).

The most interesting question I have about a Plugin is how does it technically differ from an Application?

The more I think about this question the harder time I have finding real differences. Their intentions are very different, for sure, but under the covers they don't seem to need very different moving parts. Recess will load applications to determine which plugins an application requires and then load the plugins.

One difference you bring up is the need for an end() hook called just before execution finishes. Would an Application suffer from also having this capability?  Can you expand on the scenario you describe regarding "a different controller for handling sessions".

What do you guys see as necessary technical differences between an application and a plugin?

Regarding constraint #3 "Concept Counts", if we were able to convince ourselves that there isn't too much of a difference between applications and plugins (or, that there are differences, but by reshaping our current notion of an app we could eliminate them) there are very significant upsides.

===Re: What an Extension Should Be
An extension of functionality – agreed!

·         Abstract – a plugin can’t depend on anything application specific ie. Controller names, Routing structure ... Agree and disagree. Agree from a hard dependency point of view, i.e., a plugin should not have a dependency on anything specific in an application. Disagree from a configurable point of view, i.e., an application can configure a plugin with settings a plugin needs (i.e. the route to an unauthorized request, call-backs on a model or controller, etc).

· I      Independent – a plugin should not be aware of any other plugins in the system – it should not rely on any other plugins or have any internal dependencies In the near-term I agree with this. In the far-term I can envision plugin dependencies. Not worth getting hung-up at this stage in discussion.

·         Structured – Agreed!

·         Reliable – Agreed! And applications need a better testing infrastructure as well. Is anyone a PHPUnit expert? Would love to talk further about how best to structure application-level and plugin-level tests.

===RE: Plugin API

Conventional - Agreed!

Modular - We should have discussion here on which points in the execution pipeline a plugin may want or need to tap into.

Conscious - Agreed, the extensibility points should be extremely well thought out.

===RE: Directory Structure

Unfortunately this directory structure doesn't fit with the convention Library uses. Also, the flat files break rule #1. We can achieve the same functionality with start,end,meta being specified as methods on the Plugin class correct? Also, periods cannot be used within classnames in Library (or PHP). I'm not quite sure what the intended functionality is for Plugin.Model.class.php -- are you intending to override Recess' Model class itself?  Or was this a generic way of saying the plugin can have models extending from model?

I think I'm starting to get a better picture of your intentions and suggestions for adding system-level functionality. To make progress I still think we need to identify key scenarios of plugins. Our current scenarios (taggable, authentication, validations) can all be accomplished with minor modifications to plugins as 'apps-within-an-app' and annotations. The system level scenarios (gzip'ing output, for example) do not fit the 'apps-within-an-app' model but are very similar to Django's middleware (http://docs.djangoproject.com/en/dev/topics/http/middleware/ and http://docs.djangoproject.com/en/dev/ref/middleware/#ref-middleware).

Having written out these thoughts has brought me to the following opinions:
 * Plugins are offer practically 'drop-in' functionality that developers can leverage to
  • Piece together common components of an application (i.e. parts of an app, within an app)
  • Simplify using external libraries (i.e. a smarty plugin, or a Zend Framework Component autoloader)
  • Introduce sub-classes of commonly extended framework classes (i.e. special views or controllers that applications can use as base classes)
  • Introduce annotations that utilize the new 'wrappable' methods (i.e. validations wrap insert/update, Logging, Authentication, etc)
 * Middleware (mostly undiscussed) can be applied at the Recess level (not the application level)
  • Middleware can extend key points in the execution pipeline a la Django
  • Middleware isn't in this specific sprint
Longer e-mail than I had intended! Interested in everyone elses' thoughts.

Thanks for kicking off the discussion Jamie!

Kris




KevBurnsJr

unread,
Mar 26, 2009, 10:12:28 PM3/26/09
to Recess PHP Framework
So here's my 2 cents...

Take a look at this module ( aka plugin ) for Kohana, aptly named
Simple_Auth
http://projects.kohanaphp.com/wiki/17/Using_examples

You'll notice that the module contains no controllers or views, and
offers a collection of static methods to be used by the application
for logging a user in/out.

I think the difference between this simple auth module and the one
I've drafted is a good example of two different paths a plugin might
take. Though I don't know, perhaps my Simple Auth would better be
dubbed an Application than a Plugin since it claims responsibility for
one or more routes? Maybe responsibility for a route is the line in
the sand between a Plugin and an Application?

Re: Plugin dependencies I'll throw this one out there...

Technoweenie authored a plugin called 'acts_as_versioned', which adds
versioning (versionability?) to an activerecord model in Rails.
http://github.com/technoweenie/acts_as_versioned

A plugin with a very precise function such as this makes a lot of
sense in that it's extremely versatile, and would probably play a
critical role in the creation of any Wiki. Now, if someone were to
create a Wiki plugin (containing controllers views and routes), it
would absolutely make sense for the Wiki plugin to declare its
dependency upon a Recess equivalent of acts_as_versioned.

But going back to the line-in-the-sand above (the difference between
an app and a plugin being a route), maybe it doesn't make sense for a
wiki to be a plugin. Maybe it would make more sense to keep concepts
of apps and plugins separate, and let each remain just as easy to plug-
and-play.

If I were to follow this line of reason in my SimpleAuth plugin/app,
what I would probably do is extract out all of the models and static
classes into a plugin, and leave the controllers and views in a
separate (but equally modular) app. The app would need to declare
that it depends on the existance of at least one plugin that
implements a given interface (such as the login/logout api provided by
the Kohana module above).

This probably means that plugin inclusion would happen alongside
application inclusion in recess-conf in a similar manner but to be
handled separately.

RE: Jamie's notes on reliability and convention ...

Separating plugins and apps in this manner would allow for the
interface a plugin provides to also specify a set of exceptions that
the plugin agrees to throw.

RE: _init and _close ...

Wordpress deals with this using "Plugin Hooks" which is basically a
way of registering callbacks for events. I think your idea for _init
and _close would be handled in WordPress with two specific Plugin
Hooks.

Apart from init and close, Wordpress 2.7 also offers 798 others...
http://adambrown.info/p/wp_hooks/version/2.7

As impressive as this list may be, it's important to remember that
Wordpress is an application, not a framework. I get the feeling that
Kris's method wrapping will encompass access to most if not all of the
types of events represented in WordPress's plugin hook collection
without having to call them out explicitly.

RE: Principle #3: Recess Will Minimize Concept Counts Wherever
Possible

I've always felt that this was the single most important part of any
framework. I use singletons such as CurrentUser with method names
such as isLoggedIn() so that I can write code that a 4-year-old could
understand.

if(CurrentUser::isLoggedIn()) {
$party.throw();
}

^ oh look, it's a surprise party...

(sorry, i'm just being silly now, i digress)

- Kev
Reply all
Reply to author
Forward
0 new messages