Video icon 64
Learn to Code. Start your free trial today.
Advertisement

Building your First ExpressionEngine Plugin

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

No matter how awesome a CMS is, there will be times when you are required to meet a specific need that simply doesn't work out of the box. Here we will explore creating Plugins for ExpressionEngine to meet such needs.

Version Notes

Just recently EllisLab, the creators of ExpressionEngine, announced the official release date of ExpressionEngine 2.0 as being December 1st, 2009. Please note that this tutorial is written with details specific to versions 1.6.x. Certain details such as file paths and global objects will be changing for version 2.0. While these details will be different the general application and approach to plugin creation for ExpressionEngine will remain the same.

Types of ExpressionEngine Add-Ons

Like most robust management systems available, ExpressionEngine, or "EE", comes with a number of ways for a developer to add functionality that may not be there by default. Unlike popular systems like WordPress, EE has different names for these add-ons depending on how they interact with your site. There are three primary ways you can add functionality to EE 1.6.x which are Plugins, Extensions and Modules. Plugins primarily deal with modifying your template code and tend to be more of a front-end job. Extensions typically tweak the functionality of the back-end, or Control Panel by tying into "hooks" in the system. Modules tend to be larger in scope, have their own area in the Control Panel and can also be used on the front-end of the EE website. Modules can be full blown apps sitting within EE and utilizing membership, templates, database, etc.

Required Experience

Things it would sure help to know before moving forward:

  • How to setup and install ExpressionEngine in a development environment
  • Basic understanding of ExpressionEngine's Control Panel and template parsing
  • Basic understanding of PHP and Object Oriented programming
  • How to read documentation!

Our Plugin

For this tutorial we are going to create a simple plugin that scans through the data passed to it and wraps certain strings with HTML tags. This is a very simple application of an EE plugin and should show you how easy it is to get started with plugin development. Our plugin will be used to search for and replace acronyms that we use as developers. For the sake of the example we will stick to three: HTML, CSS, and RSS. Let's call the plugin "Auto Acronym."

Where Do I Start?

The best place to start with EE Add-On development is the official ExpressionEngine Documentation. Before you dive into developing for ExpressionEngine you should read through their guidelines which, if followed, help keep the system running well. The guidelines cover things like naming conventions, performance and security. I'll give you a few minutes to read over the ExpressionEngine Add-On Development Guidelines

Okay, now that you've read that we can start looking at how we actually create the plugins and what makes up a simple one.

Pieces of the Puzzle

Every plugin requires one class and at least one function. The naming convention is very specific so we need to make sure we do it correctly and carefully. The name of our plugin is Auto Acronym and it comes into play with our file name, class name, and EE tags. The file name takes the name of your plugin and replaces all spaces with underscores while prepending "pi." to the front. All letters must be lower case. That would make our file name "pi.auto_acronym.php". All EE plugins go into a single directory located under system/plugins (imagine that).

The class name is similar to the file name with the exception of the first letter being capitalized. This makes our class name "Auto_acronym". This plugin will only need one function so it will have the same name as the class. These will also be the names we use in our EE plugin tags. Plugin tags always start with "exp:" which is telling the template parser, "Hey! I need some processing over here!". What follows will be the name of your class all lowercase giving us: "exp:auto_acronym". The third segment of a plugin tag calls the function within the plugin's class. In our example we will just be using the one function, the constructor, so we only need the class name. So far here is what we know about our plugin:

Putting it to Code

Let's see what our code is so far:

<?php

class Auto_acronym
{
   
   function Auto_acronym()
   {
      
   }
   
}

/* End of file pi.auto_acronym.php */
/* Location: ./system/plugins/pi.auto_acronym.php */

For those of you who are familiar with WordPress plugin development, you know that WordPress extracts some data from commented code when displaying details in the Plugin Manager. ExpressionEngine does something similar except with an array within your plugin file. Let's go ahead and add that to our file above the class and give it the appropriate information.

<?php

$plugin_info       = array(
   'pi_name'        => 'Auto Acronym',
   'pi_version'     => '1.0',
   'pi_author'      => 'Erik Reagan',
   'pi_author_url'  => 'http://erikreagan.com',
   'pi_description' => 'Automatically wraps certain acronyms in the HTML <acronym> tag',
   'pi_usage'       => Auto_acronym::usage()
   );

class Auto_acronym
{

   function Auto_acronym()
   {

   }

}

/* End of file pi.auto_acronym.php */
/* Location: ./system/plugins/pi.auto_acronym.php */

Each of the array keys should be pretty obvious as far as what they are for. The last key, however, is a little different. It's obviously not just a string like the others. It is used to tell the ExpressionEngine control panel where to find the usage, or simple documentation, for the plugin. Every EE plugin should have some basic docs within the plugin file so that those using the plugin can just view it in the Control Panel and see how to utilize the tags. This is done by adding a function to your plugin class called "usage" which . I like to put the usage function last in my class so let's go ahead and copy & paste the example from the ExpressionEngine Plugin Docs and remove the content between the ?> and <?php tags since our content will go there.


   // ----------------------------------------
   //  Plugin Usage
   // ----------------------------------------

   // This function describes how the plugin is used.
   //  Make sure and use output buffering

   function usage()
   {
   ob_start(); 
   ?>

   

   <?php
   $buffer = ob_get_contents();

   ob_end_clean(); 

   return $buffer;
   }

Our usage does will be written after we complete our plugin. For now let's just put in some text for testing purposes. Keep in mind that this text will be treated as pre-formatted and all characters are converted into HTML entities; HTML will not work. Let's look at our plugin in its entirety so far

<?php

$plugin_info       = array(
   'pi_name'        => 'Auto Acronym',
   'pi_version'     => '1.0',
   'pi_author'      => 'Erik Reagan',
   'pi_author_url'  => 'http://erikreagan.com',
   'pi_description' => 'Automatically wraps certain acronyms in the HTML <acronym> tag',
   'pi_usage'       => Auto_acronym::usage()
   );

class Auto_acronym
{

   function Auto_acronym()
   {

   }
   
   
   // ----------------------------------------
   //  Plugin Usage
   // ----------------------------------------

   // This function describes how the plugin is used.
   //  Make sure and use output buffering

   function usage()
   {
   ob_start(); 
   ?>

   This is where our simplified documentation will go

   <?php
   $buffer = ob_get_contents();

   ob_end_clean(); 

   return $buffer;
   }

}

/* End of file pi.auto_acronym.php */
/* Location: ./system/plugins/pi.auto_acronym.php */

Now that we have a bare-bones plugin file we need to save it to our system/plugins directory to make sure it's being read by the system properly. After saving it you will see it under Admin > Utilities > Plugin Manager. If you click on our new plugin you should see a page like this that contains the information we just added to our plugin

Making it Work

Now that our plugin is being pulled into the ExpressionEngine system properly, we can make it actually do something. Every plugin returns some form of data. This data will always come from a standardized variable that EE will look for called "return_data". The first thing we want to do is define it before our first function within the class. Then, just for testing purposes, we will use it within our first function to return a simple string.

class Auto_acronym
{
   
   var $return_data  = "";
   
   function Auto_acronym()
   {
      $this->return_data = "Just making sure this works";
      
   }

Now that we have some data being returned we can test it on the front end of our website. I am using a very minimal template for testing purposes right now but you can use this within any of your ExpressionEngine templates. If you recall from earlier our plugin tag is {exp:auto_acronym} and since we only have one function we do not need to use a third segment in this plugin. Here's what my template code looks like followed by what my browser shows:

<h1>Plugin Tutorial</h1>

<p>{exp:auto_acronym}</p>

Not exactly ground-breaking but it is getting us one step closer to our goal. Now that we know how to pass data back we can look at grabbing data passed to our plugin.

Processing Data with our Plugin

The first method we will focus on with processing data is where ExpressionEngine reads all of the data between the plugin tag pair. I you don't already know, our tag pair is very similar to how HTML tag pairs work. We have an open tag followed by data followed by a close tag like this:

{exp:auto_acronym}
This gets processed
{/exp:auto_acronym}

ExpressionEngine makes it very easy to get this data with the use of the Template Object. In this case we will need to make it global within our function so that we have access to it. Then we define our variable by calling in to play the "tagdata" variable. This variable is the data between our open and close tag pair. Take a look:

function Auto_acronym()
{
   global $TMPL;

   $data = $TMPL->tagdata;
}

Now lets do something very simple with our data and make it bold since it is just text right now. We will add the <strong> tags around it and then define our return_data variable accordingly. This is what our code, template and rendered page should look like now:

Plugin:

function Auto_acronym()
{
   global $TMPL;

   $data = '<strong>'.$TMPL->tagdata.'</strong>';
   
   $this->return_data = $data;
}

Template:

<h1>Plugin Tutorial</h1>

{exp:auto_acronym}
<p>This gets processed</p>
{/exp:auto_acronym}

Rendered in Browser:

Do Something Already!

Okay, okay. Let's actually have some fun with this now. Our goal with this plugin is to scan through the data passed to it for common web development acronyms. I mentioned that we would be using 3 for the tutorial: HTML, CSS & RSS. The first this we want to do is put them all into an array within our function. Note that if we were using this array across the plugin and had multiple functions we would probably want to store this outside of the constructor so it could be called by any of the functions.

function Auto_acronym()
{
   global $TMPL;

   $data = $TMPL->tagdata;
   
   $acronyms = array(
      'HTML' => 'HyperText Markup Language',
      'CSS' => 'Cascading Style Sheets',
      'RSS' => 'Really Simple Syndication'
      );
}

Now that we have our tag data along with the array of acronyms we need to run a foreach() loop on the acronyms. The first thing we will do is run the acronym and tagdata through a strpos() function to make sure that the acronym is, in fact, within our string. The reason we do this is so that we don't run unneeded string replacements. If your acronym dictionary had hundreds of values and you were running it through a lengthy article it could risk unnecessary processing time. If and only if the acronym is within our tag data we will use the str_replace() function to actually add in the <acronym> tags. After our foreach() loop we will define our return_data variable accordingly. Here's what it looks like:

function Auto_acronym()
{
   global $TMPL;

   $data = $TMPL->tagdata;

   $acronyms = array(
      'HTML' => 'HyperText Markup Language',
      'CSS' => 'Cascading Style Sheets',
      'RSS' => 'Really Simple Syndication'
      );
      
   foreach ($acronyms as $short => $long)
   {
      if (strpos($data, $short) !== FALSE)
      {
         $data = str_replace($short, '<acronym title="'.$long.'">'.$short.'</acronym>', $data);
      }         
   }
   
   $this->return_data = $data;
   
}

In order to test this properly we will need to change our template so that it actually has some of our desired acronyms in it. I'm going to change my template to this:

<h1>Plugin Tutorial</h1>

{exp:auto_acronym}
<p>My name is Erik and I am an addict. I stay up late into the night marking up HTML and CSS with magical alignment.
Whitespace speaks volumes of my care and finesse of the code. My RSS subscribers wait on their toes for my next
example of code beauty. Okay...not really.</p>
{/exp:auto_acronym}

Saving our template and refreshing our browser should produce this:

Now We're Getting Somewhere!

Now that we are properly processing the tags between the open and close tags of our plugin, let's look at the other option: passing data within a single tag. Using a single tag with plugins will require adding in a parameter so that data is still passed through to the plugin to be process. You can name the parameter whatever you want. In our case we will use this and add it to our template:

{exp:auto_acronym data="HTML"}

Now that we have a single tag we need to find a way to use it in our plugin. Any parameter that you want to use in your plugin can be fetched using the same Template Object that we used earlier. Instead of being available via a variable you need to fetch it with a function called fetch_param(). We want our pluign to be usable with either a single tag or a tag pair so we will take that into account when we define our $data variable. This is what we will change:

// Original 
$data = $TMPL->tagdata;

// New
$data = ($TMPL->fetch_param('data')) ? $TMPL->fetch_param('data') : $TMPL->tagdata ;

If you are not familiar with this syntax is basically says "if the data parameter exists (or returns true) then use it as our data variable, otherwise use the tagdata between tag pairs." This one change will enable us to use both tag pairs and single tags with the data parameter defined. Here's what our template should render now:

Almost Done!

Now we have a fully functioning ExpressionEngine plugin. The final step in completing it is adding in useful "usage" information. Let's go down to the usage() function and add in some simple documentation for the Control Panel.

function usage()
{
   ob_start(); 
?>

The "dictionary" of acronyms is stored in an array within the plugins/pi.auto_acronym.php file.

Automatically turn a string into an HTML acronym if it is within our acronym dictionary. You can do this with individual words or large blocks of text.


Simple Example
===========================

{exp:auto_acronym data="HTML"}

This outputs:
<acronym title="Hypertext Markup Language">HTML</acronym> in your ExpressionEngine template.



Large Block Example
===========================

{exp:auto_acronym}

<p>My name is Erik and I am an addict. I stay up late into the night marking up HTML and CSS with magical alignment.
Whitespace speaks volumes of my care and finesse of the code. My RSS subscribers wait on their toes for my next
example of code beauty. Okay...not really.</p>

{/exp:auto_acronym}

This outputs:
<p>My name is Erik and I am an addict. I stay up late into the night marking up <acronym title="Hypertext Markup Language">HTML</acronym> and <acronym title="Cascading Style Sheets">CSS</acronym> with magical alignment.
Whitespace speaks volumes of my care and finesse of the code. My <acronym title="Really Simple Syndication">RSS</acronym> subscribers wait on their toes for my next
example of code beauty. Okay...not really.</p>
<?php
   $buffer         = ob_get_contents();

   ob_end_clean(); 

   return $buffer;
}

Now our control panel should look something like this:

Congratulations!

You've now completed your first ExpressionEngine plugin. Hopefully this will open up the doors for you now that you see how easy it is. Fetching and returning data is very simple and you can run a lot of your PHP magic from within the plugin itself. I am hosting this plugin on my GitHub account where you can download the full plugin code there if you would like. Next in our ExpressionEngine Add-On series we will look at creating a simple Extension for the Control Panel. Stay tuned!

P.S.

Don't forget to check out these other awesome ExpressionEngine tutorials!


Advertisement