Learning Joomla! 3 Extension Development(Third Edition)
上QQ阅读APP看书,第一时间看更新

Creating the plugin PHP file

So now we need to write the PHP file that does all the hard work for this plugin and implements the functionality that we are trying to achieve. In your plg_content_clicktocall folder, create the file clicktocall.php with the following code:

<?php
defined('_JEXEC') or die;

jimport('joomla.plugin.plugin');

class plgContentClicktocall extends JPlugin
{
  function plgContentClicktocall( &$subject, $params )
  {
    parent::__construct( $subject, $params );
  }

  public function onContentPrepare($context, &$row, &$params, $page = 0)
  {
    // Do not run this plugin when the content is being indexed
    if ($context == 'com_finder.indexer')
    {
      return true;
    }

    if (is_object($row))
    {
      return $this->clickToCall($row->text, $params);
    }
    return $this->clickToCall($row, $params);
  }

  protected function clickToCall(&$text, &$params)
  {
    // matches 4 numbers followed by an optional hyphen or space, 
    // then followed by 4 numbers.
    // phone number is in the form XXXX-XXXX or XXXX XXXX
    $pattern = '/(\W[0-9]{4})-? ?(\W[0-9]{4})/';

        $replacement = '<a href="tel:$1$2">$1$2</a>';
        $text = preg_replace($pattern, $replacement, $text);

        return true;
  }
}

Normally you would expect to see DocBlocks before each class and function, but I've excluded these in this book just to simplify the code. You'll notice that all of the core code has these DocBlock comments which makes it easy for automated tools to generate documentation of APIs, and it also helps some IDEs to provide code completion. In third-party extensions, they are optional but commonly used, and it is recommended that you include them. Here is an example of a typical DocBlock.

/**
 * ClickToCall Content Plugin
 *
 * @package     Joomla.Plugin
 * @subpackage  Content.clicktocall
 * @since       3.0
 */

Now if we examine the code in the PHP file, you'll see the defined('_JEXEC') or die which should appear at the top of every PHP file in your extension. It is a security risk if you leave these out as they prevent the PHP file from being executed directly and force it to only run when called by Joomla!. In the past it was common to write a message with the die statement, for example, die( 'Restricted access' ), but now it is recommended to add just die to a blank page and give the potential hacker as little information as possible. A hacker seeing the text Restricted access might be able to determine that the website is using Joomla! 1.5, whereas a blank page does not reveal anything.

The next line of code you will see is the jimport call that loads the standard Joomla! plugin classes from the core.

jimport('joomla.plugin.plugin');

We then extend the standard JPlugin class to create our own unique class for our plugin.

class plgContentClicktocall extends JPlugin

When an instance of this class is created, the __construct function is called to set it all up, and in our case, we are just loading the __construct function from the parent class in the Joomla! core. If we wanted to add in our own custom code, we could add it before or after the parent::__construct line.

function plgContentClicktocall( &$subject, $params )
{
        parent::__construct( $subject, $params );
}

Now you will see a function called onContentPrepare, which is one of the plugin events we mentioned earlier. When Joomla! is preparing an article content, it will run this plugin code prior to displaying the output on the page, so it gives us the opportunity to change this content so that what's shown in the browser is different to what's actually stored in the database. When the smart search is not running, we call the clickToCall function that does all the hard work of this plugin. When the smart search is indexing content, we want to index the original article content which doesn't need Click to Call links, so there is no need for this plugin to run. As this is a simple plugin, the code from the clickToCall function could have been put within onContentPrepare, but in most cases, you are trying to do something a bit more complex, so it's better to separate it.

public function onContentPrepare($context, &$row, &$params, $page = 0)
{
  // Do not run this plugin when the content is being indexed
  if ($context == 'com_finder.indexer')
  {
    return true;
  }

  if (is_object($row))
  {
    return $this->clickToCall($row->text, $params);
  }
  return $this->clickToCall($row, $params);
}

Now we finally come to the code that identifies a phone number in the article contents and replaces it with our Click to Call link. We are using a regular expression pattern (regex) to identify the phone number in the article. Regex code can be quite complex and difficult to understand, which is why I've added lots of comments to explain the purpose of regex, which will make it easier if someone wanted to modify this later, for example, to include an area code in the phone number identification. Once the phone number has been identified, it is replaced using a standard PHP function preg_replace.

protected function clickToCall(&$text, &$params)
{
  // matches 4 numbers followed by an optional hyphen or space, 
  // then followed by 4 numbers.
  // phone number is in the form XXXX-XXXX or XXXX XXXX
  $pattern = '/(\W[0-9]{4})-? ?(\W[0-9]{4})/';

        $replacement = '<a href="tel:$1$2">$1$2</a>';
        $text = preg_replace($pattern, $replacement, $text);

        return true;
}