Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

History and issues

There are three basic types of plugin architectures in ZF

  • Chains – typically, you attach one or more "plugins" to an object, and at specified "hooks", each plugin is run. This is really simply a pubsub system. Examples include validators, filters, decorators, etc.
  • Helpers – basically, you attach one or more plugins to an object, and then that object calls these on-demand, by name. Examples are action and view helpers. These typically follow the strategy pattern when invoked.
  • Adapters – in this case, plugins are specified by name or configuration, and a builder then instantiates the given adapter with the given options. Examples include form display groups, subforms, and elements; database adapters; translation adapters; etc.

In many cases, there are combinations of patterns. For instance, with validators in form elements, we use a builder factory to create the appropriate validator object, and it is then attached to a chain. Similarly, with helpers, a "short" name is resolved to a fully qualified classname, an object instantiated, and then the appropriate method invoked.

Unfortunately, we have a few hurdles and issues with these, particularly when it comes to consistency.

  • While many components use the PluginLoader to resolve short names to class names, there are case inconsistencies that occur between components.
  • The various helpers do not follow the same paradigms when it comes to the strategy pattern: action helpers use a "direct" method; view helpers use a method named after the helper class's short name; validators use "isValid()", filters use "filter()".
  • The PluginLoader is incredibly inefficient, as it requires Zend_Loader::isReadable() lookups for each prefix path registered – this is particularly noticeable when you have multiple paths, and the match exists several paths into the stack.
  • No common paradigm for adapter constructors and/or configuration. This is the primary use case for a "unified constructor": to make adapters consistent across components.
  • No common paradigm for passing information to factories.

Recommendations

Plugin name resolution

  • Use namespaces only. Plugin lookups should only consider the namespace, and attempt to autoload from those namespaces. E.g.:
    • Use un/registerNamespace() instead of addPrefixPath()
    • Use class_exists() only; no actual lookups
  • Use lowercase-dash-separated-names. Plugin lookups should translate such names to MixedCase. E.g.:
  • Plugin lookups should ignore underscores. If an underscore is found in the "short name" provided, it will be ignored; resolution to subdirectories will not be allowed.

Use __invoke() for helpers

  • Helpers should use __invoke() in all cases. This makes usage of helpers consistent, as well as simple:
  • In cases where there is an existing interface, or where defining a method * makes better semantic sense, __invoke() should proxy to that method:

"Configurable" interface for all adapters

  • All adapters should implement a "Configurable" interface, which allows passing
    configuration options.
  • Factories will instantiate an adapter, and then pass it options.
    • As such, the adapter constructor typically should not be defined.
    • All factories should accept options as either an array or Config object; the options should include a "type" key and a "params" key; the former will be the adapter type, the latter the options with which to configure it.
    • In most cases, direct instantiation of the adapter should be allowed.
  • Adapter factories should have an attached plugin loader, and allow attaching a different one, for purposes of plugin resolution.

Chains

  • Reference implementation
  • All chains should extend one of the classes in the phly\pubsub component (to be renamed and added to ZF). These chains include:
    • Provider: subscribe to one or more topics and either notify all subscribers or filter a value through all subscribers to a topic
    • FilterChain: like Provider, but no topics; good for classes that have a single plugin flex-point.
  • Build default chains where applicable: form decorators, etc.
  • Allow extending chains and attaching subclasses to objects consuming them
    • Corollary: do not proxy to chains, but instead simply attach chains
  • Chains should act as plugin loaders or attach plugin loaders to ensure name resolution works consistently.
Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.