How to develop Plugins for Osclass

As described in my earlier post, Osclass is a free, open-source, web application written in PHP for classified ads. One of the strong sides of it is that you have the options to create and install custom plugins to extend and alter the behavior of the application. There is an integrated market where existing plugins can be easily downloaded and installed. Here are some of my own creations (more are being updated and are on the way).

I started by creating plugins for my own personal use. Since I was the only one using them I often left configuration options in configuration files instead of building full fledged admin interfaces.

But as time went by people started asking me if they could have a copy of different plugins I had made. This would require me to add functionality to them to make them easily managed by other people. Often people who weren’t developers.

One thing that struck me when I was writing new plugins was the official way that they are to be written which I thought had several issues. Since I never thought I would make many plugins, and especially not any released ones, I swallowed my pride and stuck to their regime. But now when I found myself planning more plugins I felt I wanted to streamline the experience of creating plugins for Osclass.

Bellow I will walk you through creating a very simple plugin using the official way. Later I will describe the ideas I have come up with to further streamline and isolate the plugins. The plugin we will be building will be a simple Hello World type plugin with the added option that we will dynamically set the message to show the user.

File structure

To create a plugin you first create a folder under the oc-content/plugins folder, you can call it whatever you want. Within that folder you will need to place at least one file, index.phpThat is basically the only requirements regarding the file structure. You could add several files and libraries, java script files, css files etc.

Conventions and guidelines

Due to the fact that functions in your index.php will all be public, you do best in choosing a prefix that ensures that your function don’t end up having the same name as any other function. If your plugin would contain a function install() and another plugin would do the same you would run into problems. Whatever convention you choose to pick your prefix is up to you. But prefixing with some developer identifier and/or the plugin name is one way to go.

Later when we look at my alternate way of writing plugins we will see how we can pretty much circumvent this issue altogether.

Information block

Each plugin in Osclass will have a information block in their index.php file. Here’s an example of ours. More options exist and depending on your plugin not all will be needed.

/*
Plugin Name: dliTest
Plugin URI: http://www.foobar.org/
Description: A small test plugin
Version: 1.0.0
Author: Daniel Liljeberg
Author URI: http://www.danieliljebergorg/
Short Name: dliTest
Plugin update URI: dli-test
Support URI: http://support.foobar.org
*/

This information is used internally by Osclass and is for example presented in the admin interface.

Make your plugin installable

To make it possible for a plugin to be installed you have to add a piece of code to your index.php file.

osc_register_plugin(osc_plugin_path(__FILE__), 'dliTest_install');

What this does is basically to register our plugin as being able to be installed and when a user chooses to install our plugin Osclass will run the function dliTest_install which we don’t have yet. So let’s add that to our index.php. In the same way we can add a function to be run when our plugin is uninstalled by a user. So we’ll go ahead and do that to.

/*
Plugin Name: dliTest
Plugin URI: http://www.foobar.org/
Description: A small test plugin
Version: 1.0.0
Author: Daniel Liljeberg
Author URI: http://www.danieliljebergorg/
Short Name: dliTest
Plugin update URI: dli-test
Support URI: http://support.foobar.org
*/

// Plugin functions
function dliTest_install() {
}

function dliTest_uninstall() {
}

// Register hooks
osc_register_plugin(osc_plugin_path(__FILE__), 'dliTest_install');
osc_add_hook(osc_plugin_path(__FILE__)."_uninstall", 'dliTest_uninstall');

Now we are able to install and uninstall our plugin, although it won’t actually do much.

Hooks

Osclass uses the concept of hooks to enable you to hook in different parts of your plugin to be run at different points during the applications execution.

The normal syntax for registering a hook is

osc_add_hook('hook_name', 'function_name');

Our call to osc_register_plugin in order to make it possible to install our plugin is actually just a special case for registering a hook to handle the installation. But it also handles some other functionality and is therefore wrapped in its own function.

As you can see the uninstall function above is registered using the normal osc_add_hook() function, although it does have a bit of a special syntax where the name must be the your_plugins_folder/index.php.

There are a ton of hooks you can use. For instance there are hooks that run in the page header, in the page footer, when the user is visiting an item page, when a new item is posted etc.

Look here for a complete list of hooks.

So a hook has a name and you supply it a function name. Then it makes code your have written in your plugin run at specific places in the application execution flow. This might look very magical, but it’s actually quite easy. When you register a hook the function name you have provided is added to a list of functions to run when the hook with that name is run. It’s basically like providing it a function pointer, but since php gladly executes a function in a multitude of ways you simply have to store the name of the function to run.

For example these are all valid in php

// Define a function
function helloWorld() {
echo "Hello World";
}

//Calls function
helloWorld();

//Also calls function
$funcName = "helloWorld";
$funcName();

Osclass then simply run a function at specific places that run a given hook.

osc_run_hook("header");

Now all functions registered to the header hook will be run at that place.

Common uses for this is to add things top the html header, like tags, loading scripts etc. Or hooking in and altering forms such as search and the form to post new items.

Deciding what runs when

One of the problems of the hooks is that there is no real way of telling when in the queue of functions to run your specific function is. It could be that it’s very important that you run your code early, before other plugins have been able to run their functions for the same hook. To help facilitate this you can pass an optional third parameter to the osc_register_hook function. The third parameter will let you set the priority on a scale between 1 and 10 for the function you just registered. But do note that a registered function that has the same priority would make it hard for you to know for sure that your function will be the first to run.

Without knowing for sure I would guess that if two functions have the same priority and register for the same hook, the one registered first will run first. The one registered first will be the one owned by the plugin that was loaded before the other one. And the order in which the plugins load is very likely to be dependent on their alphabetical sorting of their directory name in the oc-content/plugins folder.

So PluginA would load before PluginB and if PluginA registered foo to run at the hook header and PluginB registered bar to run at the same hook and they both had the same priority setfoo would most likely be run before bar.

Make our plugin print something

Many plugins simply make use of existing hooks to output at specific places. For instance, to make our plugin write something when we look at the details of an item we could use the item_detail hook. This hook run at the middle of item’s detail page. $item is passed as argument and can be used if needed.

function dliTest_item_details_hook($item) {
  echo '<h3>Hello World</h3>;
}
osc_add_hook('item_detail', 'dliTest_item_detail');

But what if we want to our plugin write Hello World in a page we have designed ourselves. In order to do that we will have to complete a couple of more steps.

Creating a route to our file

A route is basically a way for you to tell Osclass that

This url should present the user with this file

To do that in Osclass we use the function osc_add_route(). Read about it more on Using routes with Osclass.

So, let’s add a quick route to a page called helloWorldPage.php and make the route accept a parameter names message consisting of characters between a-Z. I will name the route similarly to the actual file, but this is in no way needed.

osc_add_route('helloWorldPage', 'helloWorldPage/([a-zA-Z]+)', 'helloWorldPage/{message}', osc_plugin_folder(__FILE__).'helloWorldPage.php');

Link to our new route

Let’s create a href link in the footer of the page to our new route.

To do that we register a hook to run a function that will print our link. In order to get the actual url of our route we use the function osc_route_url(). To that we can also pass an array of values so here we decide what our message variable should be.

function dliTest_footer() {
  $pageUrl = osc_route_url('helloWorldPage', array('message' => 'Hello World!'));
  echo '<a href="'. echo $pageUrl .'">Go to hello world!</a>';
} 
osc_add_hook('footer', 'dliTest_footer');

Display our message

If you refresh you site now you should see a link in your footer. But clicking it won’t quite take us where we want yet, we first have to create our helloWorldPage.php file. Save it in the same folder as your index.php file. Here we extract the message parameter sent using the Params object and echo it.

<?php
  $message = Params::getParam('message');
?>

<h3><?php echo $message?></h3>

Now you will be able to click the link and be taken to the page showing your message.

Complete listing

index.php

/*
Plugin Name: dliTest
Plugin URI: http://www.foobar.org/
Description: A small test plugin
Version: 1.0.0
Author: Daniel Liljeberg
Author URI: http://www.danieliljebergorg/
Short Name: dliTest
Plugin update URI: dli-test
Support URI: http://support.foobar.org
*/

// Plugin functions
function dliTest_install() {
}

function dliTest_uninstall() {
}

function dliTest_footer() {
  $pageUrl = osc_route_url('helloWorldPage', array('message' => 'Hello World!'));
  echo '<a href="'. echo $pageUrl .'">Go to hello world!</a>';
} 
osc_add_hook('footer', 'dliTest_footer');
// Register routes 
osc_add_route('helloWorldPage', 'hellowWorldPage/([a-zA-Z]+), 'helloWorldPage/{message}', osc_plugin_folder(__FILE__).'helloWorldPage.php'); 

// Register hooks 
osc_register_plugin(osc_plugin_path(__FILE__), 'dliTest_install'); 
osc_add_hook(osc_plugin_path(__FILE__)."_uninstall", 'dliTest_uninstall');

helloWorldPage.php

<?php
  $message = Params::getParam('message');
?>

<h3><?php echo $message?></h3>

Closing thoughts

This was a very simple plugin but the basic is the same even for larger plugins. You will end up with more hooks, more files and more routes. But this is what almost every plugin looks like. But keeping it all neat and easy for others to comprehend becomes quite hard when the plugins grow. We will look into this problem and how we can solve it in the next post.

Comments

Share on activity feed

Powered by WP LinkPress

Comments

Share on activity feed

Powered by WP LinkPress