zircote.com

development (in)action

Writing a Custom Provider

I decided I wanted to have a CLI interface to my Skulk library and provide a method to send messages from scripts without requiring any additional coding overhead. To do this I employed the Zend_Tool CLI [zf] and began by deciding on the functionality it would possess. After some thought, I settled ultimately that sending messages would be sufficient for the time being; however, it would require some supporting methods for api key management, naming, adding and deleting.

class Skulk_Tool_ProwlProvider extends Zend_Tool_Project_Provider_Abstract
{
}

I started the process by creating the provider class Skulk_Tool_ProwlProvider; the “Provider” portion of the name is important as the IncludePathLoader will not otherwise recognize the class as such and extending the Zend_Tool_Project_Provider_Abstract.

require_once ('Zend/Tool/Project/Provider/Abstract.php');
class Skulk_Tool_ProwlProvider extends Zend_Tool_Project_Provider_Abstract{
    public function addkey($keyname, $apikey){
    }
    public function delkey($keyname){
    }
    public function sendMessage($message, $priority = 'normal', $description = null, $url = null){
    }
}

Having established the class we may now move on to the public methods. We have established our required methods as follows:

  • Adding an api key
  • Removing an apikey
  • Sending a message
require_once ('Zend/Tool/Project/Provider/Abstract.php');

class Skulk_Tool_ProwlProvider extends Zend_Tool_Project_Provider_Abstract{

    public function addkey($keyname, $apikey){
        if(!$this->_registry->getConfig()->skulk){
            $skulk = array(
                'defaults' => array( 'priority' => 'normal'),
                'keys' => array($keyname => $apikey)
            );
            $this->_registry->getConfig ()->skulk = $skulk;
            $this->_registry->getConfig ()->save();
        } else {
            if($this->_registry->getConfig()->skulk->keys){
                $this->_registry->getConfig()->skulk->keys->$keyname = $apikey;
            } else {
                $this->_registry->getConfig()->skulk->keys = array();
                $this->_registry->getConfig()->skulk->keys->$keyname = $apikey;
            }
            $this->_registry->getConfig ()->save();
        }
    }
}

To add the API key we will need to be able to write to the configuration file in the user’s home directory or where otherwise specified at runtime. This will require interaction with the internal registry property; testing if the relevant properties exist in the configuration, modify it in the event it does or initialize it if not. We perform the various configuration operations through the _registry property as demonstrated in the example below adding the required options followed by the save method to write the configuration file.

$this->_registry->getResponse()
    ->appendContent('api key saved!', array('color' => 'green'));

We also may want to provide feedback to the user regarding the status of the operation. This also is provided through the access of the _registry property with the response object, which may be formatted for color, tab depth, centering and blocking. For the purpose of this demonstration, we will use the color decorator to make the output green, leaving our example well established and ready for the next steps.

    public function addkey($keyname, $apikey){
        $skulk = $this->_registry->getConfig()->skulk;
        if(!$skulk){
            $this->_registry->getResponse()
                ->appendContent('initializing default config for Skulk...', array('color' => 'cyan'))
                ->appendContent('default priority [normal]...', array('color' => 'cyan'));
            $skulk = array(
                'defaults' => array( 'priority' => 'normal'),
                'keys' => array($keyname => $apikey)
            );
            $this->_registry->getConfig ()->skulk = $skulk;
            $this->_registry->getConfig ()->save();
        } else {
            if($this->_registry->getConfig()->skulk->keys){
                $this->_registry->getConfig()->skulk->keys->$keyname = $apikey;
            } else {
                $this->_registry->getConfig()->skulk->keys = array();
                $this->_registry->getConfig()->skulk->keys->$keyname = $apikey;
            }
            $this->_registry->getConfig ()->save();
        }
        $this->_registry->getResponse()
            ->appendContent('api key saved!', array('color' => 'green'));
    }

Now to add the delkey method using the same tools as before, with the addition of an unset on the existing property followed by the save method, as in the addkey logic writing the file with the updated values.

    public function delkey($keyname){
        $config = $this->_registry->getConfig();
        if($config->skulk->keys->$keyname){
            unset($config->skulk->keys->$keyname);
            $this->_registry->getConfig ()->save();
            $this->_registry->getResponse()
                ->appendContent($keyname . ' apikey removed...', array('color' => 'red'));
        } else {
            $this->_registry->getResponse()
                ->appendContent($keyname . ' apikey does not exist...', array('color' => 'red'));
        }
    }

Finally this brings us to the sendMessage call, which will require ancillary methods to create the outgoing message container, the API object that sends the message as well as a priorities method to convert users input into a value expected by the API actor. The details of which are less about Providers and more about the code being employed, leaving us with a final product ready to send messages to an iOS device near you.

require_once ('Zend/Tool/Project/Provider/Abstract.php');

/**
 * @author zircote
 *
 * zf enable config.provider Skulk_Tool_ProwlProvider
 * zf create config
 * zf ? prowl
 */
class Skulk_Tool_ProwlProvider extends Zend_Tool_Project_Provider_Abstract
    implements Zend_Tool_Framework_Provider_Pretendable{
    /**
     *
     * @var Skulk_Client_Message
     */
    protected $message;
    /**
     *
     * @var Skulk_Client
     */
    protected $api;
    /**
     *
     * @example zf addkey prowl iPhone sdf234g9i24t09j23r...
     * @param string $keyname
     * @param string $apikey
     */
    public function addkey($keyname, $apikey){
        $skulk = $this->_registry->getConfig()->skulk;
        if(!$skulk){
            $this->_registry->getResponse()
                ->appendContent('initializing default config for Skulk...', array('color' => 'cyan'))
                ->appendContent('default priority [normal]...', array('color' => 'cyan'));
            $skulk = array(
                'defaults' => array( 'priority' => 'normal'),
                'keys' => array($keyname => $apikey)
            );
            $this->_registry->getConfig ()->skulk = $skulk;
            $this->_registry->getConfig ()->save();
        } else {
            if($this->_registry->getConfig()->skulk->keys){
                $this->_registry->getConfig()->skulk->keys->$keyname = $apikey;
            } else {
                $this->_registry->getConfig()->skulk->keys = array();
                $this->_registry->getConfig()->skulk->keys->$keyname = $apikey;
            }
            $this->_registry->getConfig ()->save();
        }
        $this->_registry->getResponse()
            ->appendContent('api key saved!', array('color' => 'green'));
    }

    /**
     *
     * zf delkey prowl iPhone
     * @param string $keyname
     */
    public function delkey($keyname){
        $config = $this->_registry->getConfig();
        if($config->skulk->keys->$keyname){
            unset($config->skulk->keys->$keyname);
            $this->_registry->getConfig ()->save();
            $this->_registry->getResponse()
                ->appendContent($keyname . ' apikey removed...', array('color' => 'red'));
        } else {
            $this->_registry->getResponse()
                ->appendContent($keyname . ' apikey does not exist...', array('color' => 'red'));
        }
    }

    /**
     *
     * zf send-message prowl 'test message' normal 'long description here' 'http://zircote.com'
     * @param string $message
     * @param string $priority
     * @param string $url
     * @param string $description
     */
    public function sendMessage($message, $priority = 'normal', $description = null, $url = null){
        $this->_init();
        $this->message->setApikey($this->apikeys->toArray())
            ->setEvent($message)
            ->setPriority($this->getPriority($priority))
            ->setDescription($description ? $description : $message);
        if(null !== $url){
            $this->message->setUrl($url);
        }
        $response = $this->api->add($this->message);
        $result = $response->getResult();
        if(key_exists('success', $result)){
            $this->_registry->getResponse()
                ->appendContent($response->getRemaining() . ' messages left until '
                 . $response->getResetDate(), array('color' => 'cyan'));
        } else {
            $this->_registry->getResponse()
                ->appendContent($result['error']['detail'], array('color' => 'red'));
        }

    }

    /**
     *
     * returns integer value for priorityfrom a simple string
     * @param string $priority
     * @return integer
     */
    protected function getPriority($priority){
        $priorities = array(
            'verylow' => Skulk_Client_Message::PRIORITY_VERYLOW,
            'normal' => Skulk_Client_Message::PRIORITY_NORMAL,
            'moderate' => Skulk_Client_Message::PRIORITY_MODERATE,
            'high' => Skulk_Client_Message::PRIORITY_HIGH,
            'emergency' => Skulk_Client_Message::PRIORITY_EMERGENCY
        );
        return  $priorities[strtolower($priority)];
    }

    protected function _init(){
        $this->apikeys = $this->_registry->getConfig()->skulk->keys;
        require_once 'Skulk/Client.php';
        require_once 'Skulk/Client/Message.php';
        $this->message = new Skulk_Client_Message();
        $this->api = new Skulk_Client;
    }

}

When completed it affords us the functionality list:

$ zf ? prowl
Zend Framework Command Line Console Tool v1.11.5
Actions supported by provider "Prowl"
  Prowl
    zf addkey prowl keyname apikey
    zf delkey prowl keyname
    zf send-message prowl message priority[=normal] description url