Introduction to extensions

Extensions help developers extend the functionality of applications without the need to make changes to the source code. Extensiosn are written in PHP and make use of events (also known as hooks) and filters to add additional functionality to the application and manipulate data.

Architecture

Each extension type has it's own directory in the main extensions/ directory.

Extension file

The most basic extension consists of a directory and a php file with the same name (basename).

extensions/plugins/myextension/
extensions/plugins/myextension/myextension.php

The name is case sensitive, should start with a dictionary character and must not contain any space characters.

Extension class (handler)

Every time your application's handler is built the framework will include the extension file and create an instance of {Name}{Suffix}. The most basic plugin extension can be defined as follows:

// MyExtension Plugin
Class MyExtensionPlugin {}

// PayPal PaymentGateway
Class PayPalPaymentGateway {}

// BlueLight theme
Class BlueLightTheme {}

Properties

Each extension type has different properties, however certain properties are shared by all extensions.

Name
The name of the extension. This name usually differs from the base name (which is the name of the directory and the plugin file).
Version
The version of the extension.
Description
The description of the extension.

These properties must be public so that the extension wrapper can access them.

// MyExtension Plugin
Class MyExtensionPlugin {
	var $name = 'My Extension';
	var $version = '1.0.1-rc';
	var $description = 'Capable of everything.';
}

Booting

Extensions which support booting (plugins, themes and service providers) must define a boot method which will receive 4 arguments when the extension is booted:

1. Event dispatcher
The event dispatcher makes it possible to register event listeners. Event listeners are called when a specific event is fired.
2. Filter Jar
The filter jar makes it possible to interact with data more directly. Filters are called when certain information is being processed (a new user is being created, a form is being rendered, etc.).
3. Settings Container
Contains the extension's settings. Makes it possible to easily retrieve and update settings.
4. IoC Container
Makes it possible to register routes, register bindings, make use of autoloading and more.
// MyExtension Plugin
Class MyExtensionPlugin {
	var $name = 'My Extension';

	var $version = '1.0.1-rc';

	var $description = 'Capable of everything.';

	public function boot($event, $filter, $settings, $ioc) {
		// Listen for the registration_end event
		$event->listen('registration_end', function ($user) use($settings) {
			mail($settings->email, 'Notification', $user->getName().' has signed up!');
		});

		// Attach a filter to MySampleForm
		$filter->attach('form', function ($form) {
			if ($form instanceof MySampleForm) {
				$form->checkbox('My checkbox', 'a_checkbox');
			}
		});

		// Create a new route (page)
		$ioc['router']->register('my-page/{id}', function ($id) {
			return 'My Page';
		});

		// This will save the value in the database without additional coding
		$settings->email = 'newemail@domain.com';
	}
}

Installation

The installation is the first time the extension can interact with the application. Extensions which define the install method can do the neccesary tasks to prepare the database and other resources. If no install method is defined the application will assume that the extension can be used without installation.

// MyExtension Plugin
Class MyExtensionPlugin {
	public function install() {
		// Housekeeping
	}
}

If any exceptions are thrown during installation the process will fail and the message of the exception will be displayed as the error message.

// MyExtension Plugin
Class MyExtensionPlugin {
	public function install() {
		throw new Exception("I am sneaky, you can't install me.");
	}
}

Activation and deactivation

You can do additional housekeeping by defining the activate and deactivate method.

// MyExtension Plugin
Class MyExtensionPlugin {
	public function activate() {
		// Maybe we have to extract some files
	}

	public function deactivate() {
		// Temporarily disable things but don't delete any data
	}
}

It is not recommended to remove any resources during deactivation because the extension may be reactivated.

Uninstallation

The extension must remove all resources (except static resources located in the resources directory) during uninstallation. The extension may define the uninstall method which is called before the extension's information is removed.

// MyExtension Plugin
Class MyExtensionPlugin {
	public function uninstall() {
		// Time to remove the directories we created, the database tables and the meta information
		// The application will take care of removing settings and localization data
	}
}

Autoloading

Extensions can make use of PSR4 autoloading by defining a namespace for the handler class. The autoloader will include the required file provided that the file is either in the extension's directory (extensions/plugins/myplugin/MyClass.php) or in the src/ subdirectory (extensions/plugins/myplugin/src/MyClass.php).

/**
* src/MyClass.php
**/
namespace SentoWeb\MyClass;

Class My Class {

}

/**
* myextension.php
**/
namespace SentoWeb\MyClass;

Class MyExtensionPlugin {
	public function boot() {
		$class = new MyClass; // src/MyClass.php is automatically included
	}
}

Autoloading is beneficial when the extension makes use of many classes which are defined in individual PHP files.

Dependency Injection

The extension may define a constructor method with dependencies which will be injected by the application (also referred to as constructor injection).

// MyExtension Plugin
Class MyExtensionPlugin {
	public function __construct(BBCodeParser $parser, SomethingElse $object) {
		$this->parser = $parser;
		$this->object = $object;
	}
}

The autoloader will register the namespace before the instance is constructed, making it possible to use autoloading with dependency injection.

Localization (Translation)

Localization is the process of adapting an application to support translated content. Extension localization is as easy as calling __('{type}.{basename}', '{bit}') whenever a translatable string must be included.

// Basic message
__('plugin.mysample', 'welcome_message');

// It is possible to use variables
__('plugin.mysample', 'welcome_message', array('message' => 'More Info'));

Localization Data

The extension's localization data is automatically imported during installation if the extension has the localization property defined. The property must be defined as an array where the key is the 2 letter language code and region code separated by a column and the value is either an array or the relative path of the file which returns the localization data.

// MyExtension Plugin which loads the localization data from files
Class MyExtensionPlugin {
	var $localization = array("en,US" => "languages/en.php", "hu,HU" => "languages/hu.php");
}

// MyExtension Plugin which loads the localization data from an array (useful if you only have a few translatable strings)
Class MyExtensionPlugin {
	var $localization = array("en,US" => array("key" => "Value", "something" => "Another translation"));
}
Sandbox
Sample extension showcasing the localization features.
// Sample localization file
// languages/en.php
return array('data' => 'Translation',
	'something_else' => 'Something else',
	'my_message' => 'My message :message');

Administrators can translate the extension to other languages from the admin panel without having to edit its source code.

Guidelines

  1. Due to PHP restrictions, the base name of the extension must only contain alphanumeric characters and begin with a dictionary character.
  2. Extensions developed for distribution must have non-generic names. For example an extension which creates a wiki shouldn't have "wiki" as basename, instead something less generic, like "quickwiki", should be used.

Licensing

As stated in our Software License Agreement, extensions are considered derivative work, however they remain the intellectual property of their developers. You are free to license and distribute your extensions as you see fit.