code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Google
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Google
 */

/**
 * Provides commands used to access several services offered by Google
 * including search, translation, weather, maps, and currency and general
 * value unit conversion.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Google
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Google
 * @uses     Phergie_Plugin_Command pear.phergie.org
 * @uses     Phergie_Plugin_Http pear.phergie.org
 * @uses     Phergie_Plugin_Temperature pear.phergie.org
 */
class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
{
    /**
     * Checks for dependencies.
     *
     * @return void
     */
    public function onLoad()
    {
        $plugins = $this->getPluginHandler();
        $plugins->getPlugin('Command');
        $plugins->getPlugin('Http');
        $plugins->getPlugin('Weather');
    }

    /**
     * Returns the first result of a Google search.
     *
     * @param string $query Search term
     *
     * @return void
     * @todo Implement use of URL shortening here
     */
    public function onCommandG($query)
    {
        $url = 'http://ajax.googleapis.com/ajax/services/search/web';
        $params = array(
            'v' => '1.0',
            'q' => $query
        );
        $response = $this->plugins->http->get($url, $params);
        $json = $response->getContent()->responseData;
        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();
        if ($json->cursor->estimatedResultCount > 0) {
            $msg
                = $nick
                . ': [ '
                . $json->results[0]->titleNoFormatting
                . ' ] - '
                . $json->results[0]->url
                . ' - More results: '
                . $json->cursor->moreResultsUrl;
            $this->doPrivmsg($source, $msg);
        } else {
            $msg = $nick . ': No results for this query.';
            $this->doPrivmsg($source, $msg);
        }
    }

    /**
     * Performs a Google Count search for the given term.
     *
     * @param string $query Search term
     *
     * @return void
     */
    public function onCommandGc($query)
    {
        $url = 'http://ajax.googleapis.com/ajax/services/search/web';
        $params = array(
            'v' => '1.0',
            'q' => $query
        );
        $response = $this->plugins->http->get($url, $params);
        $json = $response->getContent()->responseData->cursor;
        $count = $json->estimatedResultCount;
        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();
        if ($count) {
            $msg
                = $nick . ': ' .
                number_format($count, 0) .
                ' estimated results for ' . $query;
            $this->doPrivmsg($source, $msg);
        } else {
            $this->doPrivmsg($source, $nick . ': No results for this query.');
        }
    }

    /**
     * Performs a Google Translate search for the given term.
     *
     * @param string $from  Language of the search term
     * @param string $to    Language to which the search term should be
     *        translated
     * @param string $query Term to translate
     *
     * @return void
     */
    public function onCommandGt($from, $to, $query)
    {
        $url = 'http://ajax.googleapis.com/ajax/services/language/translate';
        $params = array(
            'v' => '1.0',
            'q' => $query,
            'langpair' => $from . '|' . $to
        );
        $response = $this->plugins->http->get($url, $params);
        $json = $response->getContent();
        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();
        if (empty($json->responseData->translatedText)) {
            $this->doPrivmsg($source, $nick . ': ' . $json->responseDetails);
        } else {
            $this->doPrivmsg(
                $source,
                $nick . ': ' . $json->responseData->translatedText
            );
        }
    }

    /**
     * Performs a Google Weather search for the given term.
     *
     * @param string $location Location to search for
     * @param int    $offset   Optional day offset from the current date
     *        between 0 and 3 to get the forecast
     *
     * @return void
     */
    public function onCommandGw($location, $offset = null)
    {
        $url = 'http://www.google.com/ig/api';
        $params = array(
            'weather' => $location,
            'hl' => $this->getConfig('google.lang', 'en'),
            'oe' => 'UTF-8'
        );
        $response = $this->plugins->http->get($url, $params);
        $xml = $response->getContent()->weather;

        $event = $this->getEvent();
        $source = $event->getSource();
        $msg = '';
        if ($event->isInChannel()) {
            $msg .= $event->getNick() . ': ';
        }

        if (isset($xml->problem_cause)) {
            $msg .= $xml->problem_cause->attributes()->data[0];
            $this->doPrivmsg($source, $msg);
            return;
        }

        $temperature = $this->plugins->getPlugin('Temperature');

        $forecast = $xml->forecast_information;
        $city = $forecast->city->attributes()->data[0];
        $zip = $forecast->postal_code->attributes()->data[0];

        if ($offset !== null) {
            $offset = (int) $offset;
            if ($offset < 0) {
                $this->doNotice($source, 'Past weather data is not available');
                return;
            } elseif ($offset > 3) {
                $this->doNotice($source, 'Future weather data is limited to 3 days from today');
                return;
            }

            $linha = $xml->forecast_conditions[$offset];
            $low = $linha->low->attributes()->data[0];
            $high = $linha->high->attributes()->data[0];
            $units = $forecast->unit_system->attributes()->data[0];
            $condition = $linha->condition->attributes()->data[0];
            $day = $linha->day_of_week->attributes()->data[0];

            $date = ($offset == 0) ? time() : strtotime('next ' . $day);
            $day = ucfirst($day) . ' ' . date('n/j/y', $date);

            if ($units == 'US') {
                $lowF = $low;
                $lowC = $temperature->convertFahrenheitToCelsius($low);
                $highF = $high;
                $highC = $temperature->convertFahrenheitToCelsius($high);
            } else {
                $lowC = $low;
                $lowF = $temperature->convertCelsiusToFahrenheit($lowC);
                $highC = $high;
                $highF = $temperature->convertCelsiusToFahrenheit($high);
            }

            $msg .= 'Forecast for ' . $city . ' (' . $zip . ')'
                . ' on ' . $day . ' ::'
                . ' Low: ' . $lowF . 'F/' . $lowC . 'C,'
                . ' High: ' . $highF . 'F/' . $highC . 'C,'
                . ' Conditions: ' . $condition;
        } else {
            $conditions = $xml->current_conditions;
            $condition = $conditions->condition->attributes()->data[0];
            $tempF = $conditions->temp_f->attributes()->data[0];
            $tempC = $conditions->temp_c->attributes()->data[0];
            $humidity = $conditions->humidity->attributes()->data[0];
            $wind = $conditions->wind_condition->attributes()->data[0];
            $time = $forecast->current_date_time->attributes()->data[0];
            $time = date('n/j/y g:i A', strtotime($time)) . ' +0000';

            $hiF = $temperature->getHeatIndex($tempF, $humidity);
            $hiC = $temperature->convertFahrenheitToCelsius($hiF);

            $msg .= 'Weather for ' . $city . ' (' . $zip . ') -'
                . ' Temperature: ' . $tempF . 'F/' . $tempC . 'C,'
                . ' ' . $humidity . ','
                . ' Heat Index: ' . $hiF . 'F/' . $hiC . 'C,'
                . ' Conditions: ' . $condition . ','
                . ' Updated: ' . $time;
        }

        $this->doPrivmsg($source, $msg);
    }

    /**
     * Performs a Google Maps search for the given term.
     *
     * @param string $location Location to search for
     *
     * @return void
     */
    public function onCommandGmap($location)
    {
        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();

        $location = utf8_encode($location);
        $url = 'http://maps.google.com/maps/geo';
        $params = array(
            'q' => $location,
            'output' => 'json',
            'gl' => $this->getConfig('google.lang', 'en'),
            'sensor' => 'false',
            'oe' => 'utf8',
            'mrt' => 'all',
            'key' => $this->getConfig('google.key')
        );
        $response = $this->plugins->http->get($url, $params);
        $json =  $response->getContent();
        if (!empty($json)) {
            $qtd = count($json->Placemark);
            if ($qtd > 1) {
                if ($qtd <= 3) {
                    foreach ($json->Placemark as $places) {
                        $xy = $places->Point->coordinates;
                        $address = utf8_decode($places->address);
                        $url = 'http://maps.google.com/maps?sll=' . $xy[1] . ','
                            . $xy[0] . '&z=15';
                        $msg = $nick . ' -> ' . $address . ' - ' . $url;
                        $this->doPrivmsg($source, $msg);
                    }
                } else {
                    $msg
                        = $nick .
                        ', there are a lot of places with that query.' .
                        ' Try to be more specific!';
                    $this->doPrivmsg($source, $msg);
                }
            } elseif ($qtd == 1) {
                $xy = $json->Placemark[0]->Point->coordinates;
                $address = utf8_decode($json->Placemark[0]->address);
                $url = 'http://maps.google.com/maps?sll=' . $xy[1] . ',' . $xy[0]
                    . '&z=15';
                $msg = $nick . ' -> ' . $address . ' - ' . $url;
                $this->doPrivmsg($source, $msg);
            } else {
                $this->doPrivmsg($source, $nick . ', I found nothing.');
            }
        } else {
            $this->doPrivmsg($source, $nick . ', we have a problem.');
        }
    }

    /**
     * Perform a Google Convert query to convert a value from one metric to
     * another.
     *
     * @param string $value Value to convert
     * @param string $from  Source metric
     * @param string $to    Destination metric
     *
     * @return void
     */
    public function onCommandGconvert($value, $from, $to)
    {
        $url = 'http://www.google.com/finance/converter';
        $params = array(
            'a' => $value,
            'from' => $from,
            'to' => $to
        );
        $response = $this->plugins->http->get($url, $params);
        $contents = $response->getContent();
        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();
        if ($contents) {
            libxml_use_internal_errors(true);
            $doc = new DOMDocument;
            $doc->loadHTML($contents);
            libxml_clear_errors();
            $xpath = new DOMXPath($doc);
            $result = $xpath->query('//div[@id="currency_converter_result"]');
            $div = $result->item(0);
            $text = rtrim($div->textContent);
            $this->doPrivmsg($source, $text);
        }
    }

    /**
     * Performs a Google search to convert a value from one unit to another.
     *
     * @param string $query Query of the form "[quantity] [unit] to [unit2]"
     *
     * @return void
     *
     * @pluginCmd [quantity] [unit] to [unit2] Convert a value from one
     *            metric to another
     */
    public function onCommandConvert($query)
    {
        $url = 'http://www.google.com/search?q=' . urlencode($query);
        $response = $this->plugins->http->get($url);
        $contents = $response->getContent();
        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();

        if ($response->isError()) {
            $code = $response->getCode();
            $message = $response->getMessage();
            $this->doNotice($nick, 'ERROR: ' . $code . ' ' . $message);
            return;
        }

        $start = strpos($contents, '
');
        if ($start !== false) {
            $end = strpos($contents, '', $start);
            $text = strip_tags(substr($contents, $start, $end - $start));
            $text = str_replace(
                array(chr(195), chr(151), chr(160)),
                array('x', '', ' '),
                $text
            );
        }

        if (isset($text)) {
            $this->doPrivmsg($source, $nick . ': ' . $text);
        } else {
            $this->doNotice($nick, 'Sorry I couldn\'t find an answer.');
        }
    }

    /**
     * Returns the first definition of a Google Dictionary search.
     *
     * @param string $query Word to get the definition
     *
     * @return void
     * @todo Implement use of URL shortening here
     */
    public function onCommandDefine($query)
    {
        $lang = $this->getConfig('google.lang', 'en');
        $url = 'http://www.google.com/dictionary/json';
        $params = array(
            'callback' => 'result',
            'q' => $query,
            'sl' => $lang,
            'tl' => $lang,
            'restrict' => 'pr,de'
        );
        $response = $this->plugins->http->get($url, $params);
        $json = $response->getContent();

        // Remove some garbage from the JSON and decode it
        $json = str_replace(array('result(', ',200,null)'), '', $json);
        $json = str_replace('"', '¿?¿', $json);
        $json = strip_tags(stripcslashes($json));
        $json = str_replace('"', "'", $json);
        $json = str_replace('¿?¿', '"', $json);
        $json = json_decode($json);

        $event = $this->getEvent();
        $source = $event->getSource();
        $nick = $event->getNick();
        if (!empty($json->webDefinitions)) {
            $results = 0;
            foreach ($json->primaries[0]->entries as $entry) {
                if ($entry->type == 'meaning') {
                    $results++;
                    if (empty($text)) {
                        foreach ($entry->terms as $term) {
                            if ($term->type == 'text') {
                                $text = trim($term->text);
                            }
                        }
                    }
                }
            }
            $more = $results > 1 ? ($results - 1) . ' ' : '';
            $lang_code = substr($lang, 0, 2);
            $msg = $nick . ': ' . $text
                 . ' - You can find ' . $more . 'more results at '
                 . 'http://www.google.com/dictionary'
                 . '?aq=f'
                 . '&langpair=' . $lang_code . '%7C' . $lang_code
                 . '&q=' . $query
                 . '&hl=' . $lang_code;
            $this->doPrivmsg($source, $msg);
        } else {
            if ($lang != 'en'){
               $lang = 'en';
               $this->onCommandDefine($query);
            } else {
               $msg = $nick . ': No results for this query.';
               $this->doPrivmsg($source, $msg);
            }
        }
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie
 */

/**
 * Handles on-demand loading of, iteration over, and access to plugins.
 *
 * @category Phergie
 * @package  Phergie
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie
 */
class Phergie_Plugin_Handler implements IteratorAggregate, Countable
{
    /**
     * Current list of plugin instances
     *
     * @var array
     */
    protected $plugins;

    /**
     * Paths in which to search for plugin class files
     *
     * @var array
     */
    protected $paths;

    /**
     * Flag indicating whether plugin classes should be instantiated on
     * demand if they are requested but no instance currently exists
     *
     * @var bool
     */
    protected $autoload;

    /**
     * Phergie_Config instance that should be passed in to any plugin
     * instantiated within the handler
     *
     * @var Phergie_Config
     */
    protected $config;

    /**
     * Phergie_Event_Handler instance that should be passed in to any plugin
     * instantiated within the handler
     *
     * @var Phergie_Event_Handler
     */
    protected $events;

    /**
     * Name of the class to use for iterating over all currently loaded
     * plugins
     *
     * @var string
     */
    protected $iteratorClass = 'Phergie_Plugin_Iterator';

    /**
     * Constructor to initialize class properties and add the path for core
     * plugins.
     *
     * @param Phergie_Config        $config configuration to pass to any
     *        instantiated plugin
     * @param Phergie_Event_Handler $events event handler to pass to any
     *        instantiated plugin
     *
     * @return void
     */
    public function __construct(
        Phergie_Config $config,
        Phergie_Event_Handler $events
    ) {
        $this->config = $config;
        $this->events = $events;

        $this->plugins = array();
        $this->paths = array();
        $this->autoload = false;

        if (!empty($config['plugins.paths'])) {
            foreach ($config['plugins.paths'] as $dir => $prefix) {
                $this->addPath($dir, $prefix);
            }
        }

        $this->addPath(dirname(__FILE__), 'Phergie_Plugin_');
    }

    /**
     * Adds a path to search for plugin class files. Paths are searched in
     * the reverse order in which they are added.
     *
     * @param string $path   Filesystem directory path
     * @param string $prefix Optional class name prefix corresponding to the
     *        path
     *
     * @return Phergie_Plugin_Handler Provides a fluent interface
     * @throws Phergie_Plugin_Exception
     */
    public function addPath($path, $prefix = '')
    {
        if (!is_readable($path)) {
            throw new Phergie_Plugin_Exception(
                'Path "' . $path . '" does not reference a readable directory',
                Phergie_Plugin_Exception::ERR_DIRECTORY_NOT_READABLE
            );
        }

        $this->paths[] = array(
            'path' => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR,
            'prefix' => $prefix
        );

        return $this;
    }

    /**
     * Returns metadata corresponding to a specified plugin.
     *
     * @param string $plugin Short name of the plugin class
     *
     * @throws Phergie_Plugin_Exception Class file can't be found
     *
     * @return array|boolean Associative array containing the path to the
     *         class file and its containing directory as well as the full
     *         class name
     */
    public function getPluginInfo($plugin)
    {
        foreach (array_reverse($this->paths) as $path) {
            $file = $path['path'] . $plugin . '.php';
            if (file_exists($file)) {
                $path = array(
                    'dir' => $path['path'],
                    'file' => $file,
                    'class' => $path['prefix'] . $plugin,
                );
                return $path;
            }
        }

        // If the class can't be found, display an error
        throw new Phergie_Plugin_Exception(
            'Class file for plugin "' . $plugin . '" cannot be found',
            Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
        );
    }

    /**
     * Adds a plugin instance to the handler.
     *
     * @param string|Phergie_Plugin_Abstract $plugin Short name of the
     *        plugin class or a plugin object
     * @param array                          $args   Optional array of
     *        arguments to pass to the plugin constructor if a short name is
     *        passed for $plugin
     *
     * @return Phergie_Plugin_Abstract New plugin instance
     */
    public function addPlugin($plugin, array $args = null)
    {
        // If a short plugin name is specified...
        if (is_string($plugin)) {
            $index = strtolower($plugin);
            if (isset($this->plugins[$index])) {
                return $this->plugins[$index];
            }

            // Attempt to locate and load the class
            $info = $this->getPluginInfo($plugin);
            $file = $info['file'];
            $class = $info['class'];
            include_once $file;
            if (!class_exists($class, false)) {
                throw new Phergie_Plugin_Exception(
                    'File "' . $file . '" does not contain class "' . $class . '"',
                    Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
                );
            }

            // Check to ensure the class is a plugin class
            if (!is_subclass_of($class, 'Phergie_Plugin_Abstract')) {
                $msg
                    = 'Class for plugin "' . $plugin .
                    '" does not extend Phergie_Plugin_Abstract';
                throw new Phergie_Plugin_Exception(
                    $msg,
                    Phergie_Plugin_Exception::ERR_INCORRECT_BASE_CLASS
                );
            }

            // Check to ensure the class can be instantiated
            $reflection = new ReflectionClass($class);
            if (!$reflection->isInstantiable()) {
                throw new Phergie_Plugin_Exception(
                    'Class for plugin "' . $plugin . '" cannot be instantiated',
                    Phergie_Plugin_Exception::ERR_CLASS_NOT_INSTANTIABLE
                );
            }

            // If the class is found, instantiate it
            if (!empty($args)) {
                $instance = $reflection->newInstanceArgs($args);
            } else {
                $instance = new $class;
            }

            // Store the instance
            $this->plugins[$index] = $instance;
            $plugin = $instance;

        } elseif ($plugin instanceof Phergie_Plugin_Abstract) {
            // If a plugin instance is specified...

            // Add the plugin instance to the list of plugins
            $this->plugins[strtolower($plugin->getName())] = $plugin;
        }

        // Configure and initialize the instance
        $plugin->setPluginHandler($this);
        $plugin->setConfig($this->config);
        $plugin->setEventHandler($this->events);
        $plugin->onLoad();

        return $plugin;
    }

    /**
     * Adds multiple plugin instances to the handler.
     *
     * @param array $plugins List of elements where each is of the form
     *        'ShortPluginName' or array('ShortPluginName', array($arg1,
     *        ..., $argN))
     *
     * @return Phergie_Plugin_Handler Provides a fluent interface
     */
    public function addPlugins(array $plugins)
    {
        foreach ($plugins as $plugin) {
            if (is_array($plugin)) {
                $this->addPlugin($plugin[0], $plugin[1]);
            } else {
                $this->addPlugin($plugin);
            }
        }

        return $this;
    }

    /**
     * Removes a plugin instance from the handler.
     *
     * @param string|Phergie_Plugin_Abstract $plugin Short name of the
     *        plugin class or a plugin object
     *
     * @return Phergie_Plugin_Handler Provides a fluent interface
     */
    public function removePlugin($plugin)
    {
        if ($plugin instanceof Phergie_Plugin_Abstract) {
            $plugin = $plugin->getName();
        }
        $plugin = strtolower($plugin);

        unset($this->plugins[$plugin]);

        return $this;
    }

    /**
     * Returns the corresponding instance for a specified plugin, loading it
     * if it is not already loaded and autoloading is enabled.
     *
     * @param string $name Short name of the plugin class
     *
     * @return Phergie_Plugin_Abstract Plugin instance
     */
    public function getPlugin($name)
    {
        // If the plugin is loaded, return the instance
        $lower = strtolower($name);
        if (isset($this->plugins[$lower])) {
            return $this->plugins[$lower];
        }

        // If autoloading is disabled, display an error
        if (!$this->autoload) {
            $msg
                = 'Plugin "' . $name . '" has been requested, ' .
                'is not loaded, and autoload is disabled';
            throw new Phergie_Plugin_Exception(
                $msg,
                Phergie_Plugin_Exception::ERR_PLUGIN_NOT_LOADED
            );
        }

        // If autoloading is enabled, attempt to load the plugin
        $plugin = $this->addPlugin($name);

        // Return the added plugin
        return $plugin;
    }

    /**
     * Returns the corresponding instances for multiple specified plugins,
     * loading them if they are not already loaded and autoloading is
     * enabled.
     *
     * @param array $names Optional list of short names of the plugin
     *        classes to which the returned plugin list will be limited,
     *        defaults to all presently loaded plugins
     *
     * @return array Associative array mapping lowercased plugin class short
     *         names to corresponding plugin instances
     */
    public function getPlugins(array $names = array())
    {
        if (empty($names)) {
            return $this->plugins;
        }

        $plugins = array();
        foreach ($names as $name) {
            $plugins[strtolower($name)] = $this->getPlugin($name);
        }
        return $plugins;
    }

    /**
     * Returns whether or not at least one instance of a specified plugin
     * class is loaded.
     *
     * @param string $name Short name of the plugin class
     *
     * @return bool TRUE if an instance exists, FALSE otherwise
     */
    public function hasPlugin($name)
    {
        return isset($this->plugins[strtolower($name)]);
    }

    /**
     * Sets a flag used to determine whether plugins should be loaded
     * automatically if they have not been explicitly loaded.
     *
     * @param bool $flag TRUE to have plugins autoload (default), FALSE
     *        otherwise
     *
     * @return Phergie_Plugin_Handler Provides a fluent interface.
     */
    public function setAutoload($flag = true)
    {
        $this->autoload = $flag;

        return $this;
    }

    /**
     * Returns the value of a flag used to determine whether plugins should
     * be loaded automatically if they have not been explicitly loaded.
     *
     * @return bool TRUE if autoloading is enabled, FALSE otherwise
     */
    public function getAutoload()
    {
        return $this->autoload;
    }

    /**
     * Allows plugin instances to be accessed as properties of the handler.
     *
     * @param string $name Short name of the plugin
     *
     * @return Phergie_Plugin_Abstract Requested plugin instance
     */
    public function __get($name)
    {
        return $this->getPlugin($name);
    }

    /**
     * Allows plugin instances to be detected as properties of the handler.
     *
     * @param string $name Short name of the plugin
     *
     * @return bool TRUE if the plugin is loaded, FALSE otherwise
     */
    public function __isset($name)
    {
        return $this->hasPlugin($name);
    }

    /**
     * Allows plugin instances to be removed as properties of handler.
     *
     * @param string $name Short name of the plugin
     *
     * @return void
     */
    public function __unset($name)
    {
        $this->removePlugin($name);
    }

    /**
     * Returns an iterator for all currently loaded plugin instances.
     *
     * @return ArrayIterator
     */
    public function getIterator()
    {
        return new $this->iteratorClass(
            new ArrayIterator($this->plugins)
        );
    }

    /**
     * Sets the iterator class used for all currently loaded plugin
     * instances.
     *
     * @param string $class Name of a class that extends FilterIterator
     *
     * @return Phergie_Plugin_Handler Provides a fluent API
     * @throws Phergie_Plugin_Exception Class cannot be found or is not an
     *         FilterIterator-based class
     */
    public function setIteratorClass($class)
    {
        $valid = true;

        try {
            $error_reporting = error_reporting(0); // ignore autoloader errors
            $r = new ReflectionClass($class);
            error_reporting($error_reporting);
            if (!$r->isSubclassOf('FilterIterator')) {
                $message = 'Class ' . $class . ' is not a subclass of FilterIterator';
                $valid = false;
            }
        } catch (ReflectionException $e) {
            $message = $e->getMessage();
            $valid = false;
        }

        if (!$valid) {
            throw new Phergie_Plugin_Exception(
                $message,
                Phergie_Plugin_Exception::ERR_INVALID_ITERATOR_CLASS
            );
        }

        $this->iteratorClass = $class;
    }

    /**
     * Proxies method calls to all plugins containing the called method.
     *
     * @param string $name Name of the method called
     * @param array  $args Arguments passed in the method call
     *
     * @return void
     */
    public function __call($name, array $args)
    {
        foreach ($this->getIterator() as $plugin) {
            call_user_func_array(array($plugin, $name), $args);
        }
        return true;
    }

    /**
     * Returns the number of plugins contained within the handler.
     *
     * @return int Plugin count
     */
    public function count()
    {
        return count($this->plugins);
    }
}

license: mit


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Help
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Help
 */

/**
 * Provides access to descriptions of plugins and the commands they provide.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Help
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Help
 * @uses     Phergie_Plugin_Command pear.phergie.org
 */
class Phergie_Plugin_Help extends Phergie_Plugin_Abstract
{
    /**
     * Registry of help data indexed by plugin name
     *
     * @var array
     */
    protected $registry;

    /**
     * Checks for dependencies.
     *
     * @return void
     */
    public function onLoad()
    {
        $this->getPluginHandler()->getPlugin('Command');
    }

    /**
     * Creates a registry of plugin metadata on connect.
     *
     * @return void
     */
    public function onConnect()
    {
        $this->populateRegistry();
    }

    /**
     * Creates a registry of plugin metadata.
     *
     * @return void
     */
    public function populateRegistry()
    {
        $this->registry = array();

        foreach ($this->plugins as $plugin) {
            $class = new ReflectionClass($plugin);
            $pluginName = strtolower($plugin->getName());

            // Parse the plugin description
            $docblock = $class->getDocComment();
            $annotations = $this->getAnnotations($docblock);
            if (isset($annotations['pluginDesc'])) {
                $pluginDesc = implode(' ', $annotations['pluginDesc']);
            } else {
                $pluginDesc = $this->parseShortDescription($docblock);
            }
            $this->registry[$pluginName] = array(
                'desc' => $pluginDesc,
                'cmds' => array()
            );

            // Parse command method descriptions
            $methodPrefix = Phergie_Plugin_Command::METHOD_PREFIX;
            $methodPrefixLength = strlen($methodPrefix);
            foreach ($class->getMethods() as $method) {
                if (strpos($method->getName(), $methodPrefix) !== 0) {
                    continue;
                }

                $cmd = strtolower(substr($method->getName(), $methodPrefixLength));
                $docblock = $method->getDocComment();
                $annotations = $this->getAnnotations($docblock);

                if (isset($annotations['pluginCmd'])) {
                    $cmdDesc = implode(' ', $annotations['pluginCmd']);
                } else {
                    $cmdDesc = $this->parseShortDescription($docblock);
                }

                $cmdParams = array();
                if (!empty($annotations['param'])) {
                    foreach ($annotations['param'] as $param) {
                        $match = null;
                        if (preg_match('/\h+\$([^\h]+)\h+/', $param, $match)) {
                            $cmdParams[] = $match[1];
                        }
                    }
                }

                $this->registry[$pluginName]['cmds'][$cmd] = array(
                    'desc' => $cmdDesc,
                    'params' => $cmdParams
                );
            }

            if (empty($this->registry[$pluginName]['cmds'])) {
                unset($this->registry[$pluginName]);
            }
        }
    }

    /**
     * Displays a list of plugins with help information available or
     * commands available for a specific plugin.
     *
     * @param string $query Optional short name of a plugin for which commands
     *        should be returned or a command; if unspecified, a list of
     *        plugins with help information available is returned
     *
     * @return void
     */
    public function onCommandHelp($query = null)
    {
        if ($query == 'refresh') {
            $this->populateRegistry();
        }

        $nick = $this->getEvent()->getNick();
        $delay = $this->getConfig('help.delay', 2);

        // Handle requests for a plugin list
        if (!$query) {
            $msg = 'These plugins have help information available: '
                 . implode(', ', array_keys($this->registry));
            $this->doPrivmsg($nick, $msg);
            return;
        }

        // Handle requests for plugin information
        $query = strtolower($query);
        if (isset($this->registry[$query])
            && empty($this->registry[$query]['cmds'][$query])) {
            $msg = $query . ' - ' . $this->registry[$query]['desc'];
            $this->doPrivmsg($nick, $msg);

            $msg = 'Available commands - '
                 . implode(', ', array_keys($this->registry[$query]['cmds']));
            $this->doPrivmsg($nick, $msg);

            if ($this->getConfig('command.prefix')) {
                $msg
                    = 'Note that these commands must be prefixed with "'
                    . $this->getConfig('command.prefix')
                    . '" (without quotes) when issued in a public channel.';
                $this->doPrivmsg($nick, $msg);
            }

            return;
        }

        // Handle requests for command information
        foreach ($this->registry as $plugin => $data) {
            if (empty($data['cmds'])) {
                continue;
            }

            $result = preg_grep('/^' . $query . '$/i', array_keys($data['cmds']));
            if (!$result) {
                continue;
            }

            $cmd = $data['cmds'][array_shift($result)];
            $msg = $query;
            if (!empty($cmd['params'])) {
                $msg .= ' [' . implode('] [', $cmd['params']) . ']';
            }
            $msg .= ' - ' . $cmd['desc'];
            $this->doPrivmsg($nick, $msg);
        }
    }

    /**
     * Parses and returns the short description from a docblock.
     *
     * @param string $docblock Docblock comment code
     *
     * @return string Short description (i.e. content from the start of the
     *         docblock up to the first double-newline)
     */
    protected function parseShortDescription($docblock)
    {
        $desc = preg_replace(
            array('#^\h*\*\h*#m', '#^/\*\*\h*\v+\h*#', '#(?:\r?\n){2,}.*#s', '#\s*\v+\s*#'),
            array('', '', '', ' '),
            $docblock
        );
        return $desc;
    }

    /**
     * Taken from PHPUnit/Util/Test.php and modified to fix an issue with
     * tag content spanning multiple lines.
     *
     * PHPUnit
     *
     * Copyright (c) 2002-2010, Sebastian Bergmann <sb@sebastian-bergmann.de>.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     *   * Redistributions of source code must retain the above copyright
     *     notice, this list of conditions and the following disclaimer.
     *
     *   * Redistributions in binary form must reproduce the above copyright
     *     notice, this list of conditions and the following disclaimer in
     *     the documentation and/or other materials provided with the
     *     distribution.
     *
     *   * Neither the name of Sebastian Bergmann nor the names of his
     *     contributors may be used to endorse or promote products derived
     *     from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     * POSSIBILITY OF SUCH DAMAGE.
     *
     * @param string $docblock docblock to parse
     *
     * @return array
     */
    protected function getAnnotations($docblock)
    {
        $annotations = array();

        $regex = '/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?(?:\*\/|\* @)/ms';

        if (preg_match_all($regex, $docblock, $matches)) {
            $numMatches = count($matches[0]);

            for ($i = 0; $i < $numMatches; ++$i) {
                $annotation = $matches['value'][$i];
                $annotation = preg_replace('/\s*\v+\s*\*\s*/', ' ', $annotation);
                $annotation = rtrim($annotation);
                $annotations[$matches['name'][$i]][] = $annotation;
            }
        }

        return $annotations;
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Http
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Http
 */

/**
 * Provides an HTTP client for plugins to use in contacting web services or
 * retrieving feeds or web pages.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Http
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Http
 * @uses     extension simplexml optional
 * @uses     extension json optional
 */
class Phergie_Plugin_Http extends Phergie_Plugin_Abstract
{
    /**
     * Response to the last executed HTTP request
     *
     * @var Phergie_Plugin_Http_Response
     */
    protected $response;

    /**
     * Mapping of content types to handlers for them
     *
     * @var array
     */
    protected $handlers;

    /**
     * Initializes the handler lookup table.
     *
     * @return void
     */
    public function onLoad()
    {
        $this->handlers = array(
            '(?:application|text)/xml(?:;.*)?'
                => 'simplexml_load_string',
            '(?:(?:application|text)/(?:x-)?json)|text/javascript.*'
                => 'json_decode',
        );

        if (is_array($this->config['http.handlers'])) {
            $this->handlers = array_merge(
                $this->handlers,
                $this->config['http.handlers']
            );
        }
    }

    /**
     * Sets a handler callback for a content type, which is called when a
     * response of that content type is received to perform any needed
     * transformations on the response body content before storing it in the
     * response object. Note that the calling plugin is responsible for
     * indicating any dependencies related to specified handler callbacks.
     *
     * @param string   $type     PCRE regular expression (without delimiters) that
     *        matches one or more MIME types
     * @param callback $callback Callback to execute when a response of a content
     *        type matched by $type is encountered
     *
     * @return Phergie_Plugin_Http Provides a fluent interface
     */
    public function setHandler($type, $callback)
    {
        if (!is_callable($callback)) {
            throw new Phergie_Plugin_Exception(
                'Invalid callback specified',
                Phergie_Plugin_Exception::ERR_FATAL_ERROR
            );
        }

        $this->handlers[$type] = $callback;

        return $this;
    }

    /**
     * Supporting method that parses the status line of an HTTP response
     * message.
     *
     * @param string $status Status line
     *
     * @return array Associative array containing the HTTP version, response
     *         code, and response description
     */
    protected function parseStatusLine($status)
    {
        $parts = explode(' ', $status, 3);
        $parsed = array(
            'version' => str_replace('HTTP/', '', $parts[0]),
            'code' => $parts[1],
            'message' => rtrim($parts[2])
        );
        return $parsed;
    }

    /**
     * Supporting method that acts as an error handler to intercept HTTP
     * responses resulting in PHP-level errors.
     *
     * @param int    $errno   Level of the error raised
     * @param string $errstr  Error message
     * @param string $errfile Name of the file in which the error was raised
     * @param string $errline Line number on which the error was raised
     *
     * @return bool Always returns TRUE to allow normal execution to
     *         continue once this method terminates
     */
    public function handleError($errno, $errstr, $errfile, $errline)
    {
        if ($httperr = strstr($errstr, 'HTTP/')) {
            $parts = $this->parseStatusLine($httperr);
            $this->response
                ->setCode($parts['code'])
                ->setMessage($parts['message']);
        }

        return true;
    }

    /**
     * Supporting method that executes a request and handles the response.
     *
     * @param string $url     URL to request
     * @param array  $context Associative array of stream context parameters
     *
     * @return Phergie_Plugin_Http_Response Object representing the response
     *         resulting from the request
     */
    public function request($url, array $context)
    {
        $this->response = new Phergie_Plugin_Http_Response;

        $url = (string) $url;
        $context = stream_context_create(array('http' => $context));

        set_error_handler(array($this, 'handleError'), E_WARNING);
        $stream = fopen($url, 'r', false, $context);
        if ($stream) {
            $meta = stream_get_meta_data($stream);
            $status = $this->parseStatusLine($meta['wrapper_data'][0]);
            $code = $status['code'];
            $message = $status['message'];
            $headers = array();
            foreach (array_slice($meta['wrapper_data'], 1) as $header) {
                if (!strpos($header, ':')) {
                    continue;
                }
                list($name, $value) = explode(': ', $header, 2);
                $headers[$name] = $value;
            }
            unset($meta['wrapper_data']);

            $this->response
                ->setCode($code)
                ->setMessage($message)
                ->setHeaders($headers)
                ->setMeta($meta);

            $body = stream_get_contents($stream);
            $type = $this->response->getHeaders('content-type');
            foreach ($this->handlers as $expr => $handler) {
                if (preg_match('#^' . $expr . '$#i', $type)) {
                    $handled = call_user_func($handler, $body);
                    if (!empty($handled)) {
                        $body = $handled;
                    }
                }
            }

            $this->response->setContent($body);
        }
        restore_error_handler();

        return $this->response;
    }

    /**
     * Performs a GET request.
     *
     * @param string $url     URL for the request
     * @param array  $query   Optional associative array of parameters
     *        constituting the URL query string if $url has none
     * @param array  $context Optional associative array of additional stream
     *        context parameters
     *
     * @return Phergie_Plugin_Http_Response Received response data
     */
    public function get($url, array $query = array(), array $context = array())
    {
        if (!empty($query)) {
            $url .= '?' . http_build_query($query);
        }

        $context['method'] = 'GET';

        return $this->request($url, $context);
    }

    /**
     * Performs a HEAD request.
     *
     * @param string $url     URL for the request
     * @param array  $query   Optional associative array of parameters
     *        constituting the URL query string if $url has none
     * @param array  $context Optional associative array of additional stream
     *        context parameters
     *
     * @return Phergie_Plugin_Http_Response Received response data
     */
    public function head($url, array $query = array(), array $context = array())
    {
        if (!empty($query)) {
            $url .= '?' . http_build_query($query);
        }

        $context['method'] = 'HEAD';

        return $this->request($url, $context);
    }

    /**
     * Performs a POST request.
     *
     * @param string $url     URL for the request
     * @param array  $query   Optional associative array of parameters
     *        constituting the URL query string if $url has none
     * @param array  $post    Optional associative array of parameters
     *        constituting the POST request body if it is using the
     *        traditional URL-encoded format
     * @param array  $context Optional associative array of additional stream
     *        context parameters
     *
     * @return Phergie_Plugin_Http_Response Received response data
     */
    public function post($url, array $query = array(),
        array $post = array(), array $context = array()
    ) {
        if (!empty($query)) {
            $url .= '?' . http_build_query($query);
        }

        $context['method'] = 'POST';

        if (!empty($post)
            && (!empty($context['header'])
            xor stripos($context['header'], 'Content-Type'))
        ) {
            if (!empty($context['header'])) {
                $context['header'] .= "\r\n";
            } else {
                $context['header'] = '';
            }
            $context['header'] .=
                'Content-Type: application/x-www-form-urlencoded';
            $context['content'] = http_build_query($post);
        }

        return $this->request($url, $context);
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Http
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Http
 */

/**
 * Data structure for HTTP response information.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Http
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Http
 */
class Phergie_Plugin_Http_Response
{
    /**
     * HTTP response code or 0 if no HTTP response was received
     *
     * @var string
     */
    protected $code;

    /**
     * HTTP response strings
     *
     * @var array
     */
    protected static $codeStrings = array(
        0   => 'No Response',
        100 => 'Continue',
        200 => 'OK',
        201 => 'Created',
        204 => 'No Content',
        206 => 'Partial Content',
        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        307 => 'Temporary Redirect',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        408 => 'Request Timeout',
        410 => 'Gone',
        413 => 'Request Entity Too Large',
        414 => 'Request URI Too Long',
        415 => 'Unsupported Media Type',
        416 => 'Requested Range Not Satisfiable',
        417 => 'Expectation Failed',
        500 => 'Internal Server Error',
        501 => 'Method Not Implemented',
        503 => 'Service Unavailable',
        506 => 'Variant Also Negotiates'
    );

    /**
     * Description of the HTTP response code or the error message if no HTTP
     * response was received
     *
     * @var string
     */
    protected $message;

    /**
     * Content of the response body, decoded for supported content types
     *
     * @var mixed
     */
    protected $content;

    /**
     * Associative array mapping response header names to their values
     *
     * @var array
     */
    protected $headers;

    /**
     * Associative array containing other metadata about the response
     *
     * @var array
     */
    protected $meta;

    /**
     * Sets the HTTP response code.
     *
     * @param string $code Response code
     *
     * @return Phergie_Plugin_Http_Response Provides a fluent interface
     */
    public function setCode($code)
    {
        $this->code = $code;
        return $this;
    }

    /**
     * Returns the HTTP response code.
     *
     * @return string Response code
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Returns the HTTP response code text.
     *
     * @return string Response code text
     */
    public function getCodeAsString()
    {
        $code = $this->code;

        if (!isset(self::$codeStrings[$code])) {
            return 'Unkown HTTP Status';
        }

        return self::$codeStrings[$code];
    }

    /**
     * Returns whether the response indicates a client- or server-side error.
     *
     * @return bool TRUE if the response indicates an error, FALSE otherwise
     */
    public function isError()
    {
        switch (substr($this->code, 0, 1)) {
        case '0':
        case '4':
        case '5':
            return true;
        default:
            return false;
        }
    }

    /**
     * Sets the HTTP response description.
     *
     * @param string $message Response description
     *
     * @return Phergie_Plugin_Http_Response Provides a fluent interface
     */
    public function setMessage($message)
    {
        $this->message = $message;
        return $this;
    }

    /**
     * Returns the HTTP response description.
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Sets the content of the response body.
     *
     * @param mixed $content Response body content
     *
     * @return Phergie_Plugin_Http_Response Provides a fluent interface
     */
    public function setContent($content)
    {
        $this->content = $content;
        return $this;
    }

    /**
     * Returns the content of the response body.
     *
     * @return mixed Response body content, decoded for supported content
     *         types
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Sets the response headers.
     *
     * @param array $headers Associative array of response headers indexed
     *        by header name
     *
     * @return Phergie_Plugin_Http_Response Provides a fluent interface
     */
    public function setHeaders(array $headers)
    {
        $names = array_map('strtolower', array_keys($headers));
        $values = array_values($headers);
        $this->headers = array_combine($names, $values);
        return $this;
    }

    /**
     * Returns all response headers or the value of a single specified
     * response header.
     *
     * @param string $name Optional name of a single header for which the
     *        associated value should be returned
     *
     * @return array|string Associative array of all header values, a string
     *         containing the value of the header indicated by $name if one
     *         is set, or null if one is not
     */
    public function getHeaders($name = null)
    {
        if ($name) {
            $name = strtolower($name);
            if (empty($this->headers[$name])) {
                return null;
            }
            return $this->headers[$name];
        }
        return $this->headers;
    }

    /**
     * Sets the response metadata.
     *
     * @param array $meta Associative array of response metadata
     *
     * @return Phergie_Plugin_Http_Response Provides a fluent interface
     */
    public function setMeta(array $meta)
    {
        $this->meta = $meta;
        return $this;
    }

    /**
     * Returns all metadata or the value of a single specified metadatum.
     *
     * @param string $name Optional name of a single metadatum for which the
     *        associated value should be returned
     *
     * @return array|string|null Associative array of all metadata values, a
     *         string containing the value of the metadatum indicated by
     *         $name if one is set, or null if one is not
     */
    public function getMeta($name = null)
    {
        if ($name) {
            if (empty($this->meta[$name])) {
                return null;
            }
            return $this->meta[$name];
        }
        return $this->meta;
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Ideone
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Ideone
 */

/**
 * Interfaces with ideone.com to execute code and return the result.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Ideone
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Ideone
 * @uses     Phergie_Plugin_Command pear.phergie.org
 */
class Phergie_Plugin_Ideone extends Phergie_Plugin_Abstract
{
    /**
     * Checks for dependencies.
     *
     * @return void
     */
    public function onLoad()
    {
        $this->plugins->getPlugin('Command');
    }

    /**
     * Checks a service response for an error, sends a notice to the event
     * source if an error has occurred, and returns whether an error was found.
     *
     * @param array $result Associative array representing the service response
     *
     * @return boolean TRUE if an error is found, FALSE otherwise
     */
    protected function isError($result)
    {
        if ($result['error'] != 'OK') {
            $this->doNotice($this->event->getNick(), 'ideone error: ' . $result['error']);
            return true;
        }
        return false;
    }

    /**
     * Executes a source code sequence in a specified language and returns
     * the result.
     *
     * @param string $language Programming language the source code is in
     * @param string $code     Source code to execute
     *
     * @return void
     */
    public function onCommandIdeone($language, $code)
    {
        $source = $this->event->getSource();
        $nick = $this->event->getNick();

        // Get authentication credentials
        $user = $this->getConfig('ideone.user', 'test');
        $pass = $this->getConfig('ideone.pass', 'test');

        // Normalize the command parameters
        $language = strtolower($language);

        // Massage PHP code to allow for convenient shorthand
        if ($language == 'php') {
            if (!preg_match('/^<\?(?:php)?/', $code)) {
                $code = '<?php ' . $code;
            }
            switch (substr($code, -1)) {
                case '}':
                case ';':
                    break;
                default:
                    $code .= ';';
                    break;
            }
        }

        // Identify the language to use
        $client = new SoapClient('http://ideone.com/api/1/service.wsdl');
        $response = $client->getLanguages($user, $pass);
        if ($this->isError($response)) {
            return;
        }
        $languageLength = strlen($language);
        foreach ($response['languages'] as $languageId => $languageName) {
            if (strncasecmp($language, $languageName, $languageLength) == 0) {
                break;
            }
        }

        // Send the paste data
        $response = $client->createSubmission(
            $user,
            $pass,
            $code,
            $languageId,
            null, // string input - data from stdin
            true, // boolean run - TRUE to execute the code
            false // boolean private - FALSE to make the paste public
        );
        if ($this->isError($response)) {
            return;
        }
        $link = $response['link'];

        // Wait until the paste data is processed or the service fails
        $attempts = $this->getConfig('ideone.attempts', 10);
        foreach (range(1, $attempts) as $attempt) {
            $response = $client->getSubmissionStatus($user, $pass, $link);
            if ($this->isError($response)) {
                return;
            }
            if ($response['status'] == 0) {
                $result = $response['result'];
                break;
            } else {
                $result = null;
                sleep(1);
            }
        }
        if ($result == null) {
            $this->doNotice($nick, 'ideone error: Timed out');
            return;
        }
        if ($result != 15) {
            $this->doNotice($nick, 'ideone error: Status code ' . $result);
            return;
        }

        // Get details for the created paste
        $response = $client->getSubmissionDetails(
            $user,
            $pass,
            $link,
            false, // boolean withSource - FALSE to not return the source code
            false, // boolean withInput - FALSE to not return stdin data
            true,  // boolean withOutput - TRUE to include output
            true,  // boolean withStderr - TRUE to return stderr data
            false  // boolean withCmpinfo - TRUE to return compilation info
        );
        if ($this->isError($response)) {
            return;
        }

        // Replace the output if it exceeds a specified maximum length
        $outputLimit = $this->getConfig('ideone.output_limit', 100);
        var_dump($response);
        if ($outputLimit && strlen($response['output']) > $outputLimit) {
            $response['output'] = 'Output is too long to post';
        }

        // Format the message
        $msg = $this->getConfig('ideone.format', '%nick%: [ %link% ] %output%');
        $response['nick'] = $nick;
        $response['link'] = 'http://ideone.com/' . $link;
        foreach ($response as $key => $value) {
            $msg = str_replace('%' . $key . '%', $value, $msg);
        }
        $this->doPrivmsg($source, $msg);
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Invisible
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Invisible
 */

/**
 * Marks the bot as invisible when it connects to the server.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Invisible
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Invisible
 * @link     http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_3_2
 */
class Phergie_Plugin_Invisible extends Phergie_Plugin_Abstract
{
    /**
     * Marks the bot as invisible when it connects to the server.
     *
     * @return void
     */
    public function onConnect()
    {
        $this->doMode($this->getConnection()->getNick(), '+i');
        $this->getPluginHandler()->removePlugin($this);
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie
 */

/**
 * Implements a filtering iterator for limiting executing of methods across
 * a group of plugins.
 *
 * @category Phergie
 * @package  Phergie
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie
 */
class Phergie_Plugin_Iterator extends FilterIterator
{
    /**
     * List of short names of plugins to exclude when iterating
     *
     * @var array
     */
    protected $plugins = array();

    /**
     * List of method names where plugins with these methods will be
     * excluded when iterating
     *
     * @var array
     */
    protected $methods = array();

    /**
     * Overrides the parent constructor to reset the internal iterator's
     * pointer to the current item, which the parent class errantly does not
     * do.
     *
     * @param Iterator $iterator Iterator to filter
     *
     * @return void
     * @link http://bugs.php.net/bug.php?id=52560
     */
    public function __construct(Iterator $iterator)
    {
        parent::__construct($iterator);
        $this->rewind();
    }

    /**
     * Adds to a list of plugins to exclude when iterating.
     *
     * @param mixed $plugins String containing the short name of a single
     *        plugin to exclude or an array of short names of multiple
     *        plugins to exclude
     *
     * @return Phergie_Plugin_Iterator Provides a fluent interface
     */
    public function addPluginFilter($plugins)
    {
        if (is_array($plugins)) {
            $this->plugins = array_unique(
                array_merge($this->plugins, $plugins)
            );
        } else {
            $this->plugins[] = $plugins;
        }
        return $this;
    }

    /**
     * Adds to a list of method names where plugins defining these methods
     * will be excluded when iterating.
     *
     * @param mixed $methods String containing the name of a single method
     *        or an array containing the name of multiple methods
     *
     * @return Phergie_Plugin_Iterator Provides a fluent interface
     */
    public function addMethodFilter($methods)
    {
        if (is_array($methods)) {
            $this->methods = array_merge($this->methods, $methods);
        } else {
            $this->methods[]= $methods;
        }
        return $this;
    }

    /**
     * Clears any existing plugin and methods filters.
     *
     * @return Phergie_Plugin_Iterator Provides a fluent interface
     */
    public function clearFilters()
    {
        $this->plugins = array();
        $this->methods = array();
    }

    /**
     * Implements FilterIterator::accept().
     *
     * @return boolean TRUE to include the current item in those by returned
     *         during iteration, FALSE otherwise
     */
    public function accept()
    {
        if (!$this->plugins && !$this->methods) {
            return true;
        }

        $current = $this->current();

        if (in_array($current->getName(), $this->plugins)) {
            return false;
        }

        foreach ($this->methods as $method) {
            if (method_exists($current, $method)) {
                return false;
            }
        }

        return true;
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Join
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Join
 */

/**
 * Joins a specified channel on command from a user.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Join
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Join
 * @uses     Phergie_Plugin_Command pear.phergie.org
 */
class Phergie_Plugin_Join extends Phergie_Plugin_Abstract
{
    /**
     * Checks for dependencies.
     *
     * @return void
     */
    public function onLoad()
    {
        $this->getPluginHandler()->getPlugin('Command');
    }

    /**
     * Joins a channel.
     *
     * @param string $channels Comma-delimited list of channels to join
     * @param string $keys     Optional comma-delimited list of channel keys
     *
     * @return void
     */
    public function onCommandJoin($channels, $keys = null)
    {
        $this->doJoin($channels, $keys);
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Karma
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Karma
 */

/**
 * Handles requests for incrementation or decrementation of a maintained list
 * of counters for specified terms.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Karma
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Karma
 * @uses     extension PDO
 * @uses     extension pdo_sqlite
 * @uses     Phergie_Plugin_Command pear.phergie.org
 */
class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
{
    /**
     * SQLite object
     *
     * @var resource
     */
    protected $db = null;

    /**
     * Prepared statement to add a new karma record
     *
     * @var PDOStatement
     */
    protected $insertKarma;

    /**
     * Prepared statement to update an existing karma record
     *
     * @var PDOStatement
     */
    protected $updateKarma;

    /**
     * Retrieves an existing karma record
     *
     * @var PDOStatement
     */
    protected $fetchKarma;

    /**
     * Retrieves an existing fixed karma record
     *
     * @var PDOStatement
     */
    protected $fetchFixedKarma;

    /**
     * Retrieves a positive answer for a karma comparison
     *
     * @var PDOStatement
     */
    protected $fetchPositiveAnswer;

    /**
     * Retrieves a negative answer for a karma comparison
     *
     * @var PDOStatement
     */
    protected $fetchNegativeAnswer;

    /**
     * Check for dependencies and initializes a database connection and
     * prepared statements.
     *
     * @return void
     */
    public function onLoad()
    {
        $plugins = $this->getPluginHandler();
        $plugins->getPlugin('Command');
        $this->getDb();
    }

    /**
     * Initializes prepared statements used by the plugin.
     *
     * @return void
     */
    protected function initializePreparedStatements()
    {
        $this->fetchKarma = $this->db->prepare('
            SELECT karma
            FROM karmas
            WHERE term = :term
            LIMIT 1
        ');

        $this->insertKarma = $this->db->prepare('
            INSERT INTO karmas (term, karma)
            VALUES (:term, :karma)
        ');

        $this->updateKarma = $this->db->prepare('
            UPDATE karmas
            SET karma = :karma
            WHERE term = :term
        ');

        $this->fetchFixedKarma = $this->db->prepare('
            SELECT karma
            FROM fixed_karmas
            WHERE term = :term
            LIMIT 1
        ');

        $this->fetchPositiveAnswer = $this->db->prepare('
            SELECT answer
            FROM positive_answers
            ORDER BY RANDOM()
            LIMIT 1
        ');

        $this->fetchNegativeAnswer = $this->db->prepare('
            SELECT answer
            FROM negative_answers
            ORDER BY RANDOM()
            LIMIT 1
        ');
    }

    /**
     * Returns a connection to the plugin database, initializing one if none
     * is explicitly set.
     *
     * @return PDO Database connection
     */
    public function getDb()
    {
        if (empty($this->db)) {
            $this->db = new PDO('sqlite:' . dirname(__FILE__) . '/Karma/karma.db');
            $this->initializePreparedStatements();
        }
        return $this->db;
    }

    /**
     * Sets the connection to the plugin database, mainly intended for unit
     * testing.
     *
     * @param PDO $db Database connection
     *
     * @return Phergie_Plugin_Karma Provides a fluent interface
     */
    public function setDb(PDO $db)
    {
        $this->db = $db;
        $this->initializePreparedStatements();
        return $this;
    }

    /**
     * Get the canonical form of a given term.
     *
     * In the canonical form all sequences of whitespace
     * are replaced by a single space and all characters
     * are lowercased.
     *
     * @param string $term Term for which a canonical form is required
     *
     * @return string Canonical term
     */
    protected function getCanonicalTerm($term)
    {
        $canonicalTerm = strtolower(preg_replace('|\s+|', ' ', trim($term, '()')));
        switch ($canonicalTerm) {
            case 'me':
                $canonicalTerm = strtolower($this->event->getNick());
                break;
            case 'all':
            case '*':
            case 'everything':
                $canonicalTerm = 'everything';
                break;
        }
        return $canonicalTerm;
    }

    /**
     * Intercepts a message and processes any contained recognized commands.
     *
     * @return void
     */
    public function onPrivmsg()
    {
        $message = $this->getEvent()->getText();

        $termPattern = '\S+?|\([^<>]+?\)+';
        $actionPattern = '(?P<action>\+\+|--)';

        $modifyPattern = <<<REGEX
		{^
		(?J) # allow overwriting capture names
		\s*  # ignore leading whitespace

		(?:  # start with ++ or -- before the term
            $actionPattern
            (?P<term>$termPattern)
		|   # follow the term with ++ or --
            (?P<term>$termPattern)
			$actionPattern # allow no whitespace between the term and the action
		)
		$}ix
REGEX;

        $versusPattern = <<<REGEX
        {^
        	(?P<term0>$termPattern)
        		\s+(?P<method><|>)\s+
        	(?P<term1>$termPattern)$#
        $}ix
REGEX;

        $match = null;

        if (preg_match($modifyPattern, $message, $match)) {
            $action = $match['action'];
            $term = $this->getCanonicalTerm($match['term']);
            $this->modifyKarma($term, $action);
        } elseif (preg_match($versusPattern, $message, $match)) {
            $term0 = trim($match['term0']);
            $term1 = trim($match['term1']);
            $method = $match['method'];
            $this->compareKarma($term0, $term1, $method);
        }
    }

    /**
     * Get the karma rating for a given term.
     *
     * @param string $term Term for which the karma rating needs to be
     *        retrieved
     *
     * @return void
     */
    public function onCommandKarma($term)
    {
        $source = $this->getEvent()->getSource();
        $nick = $this->getEvent()->getNick();

        $canonicalTerm = $this->getCanonicalTerm($term);

        $fixedKarma = $this->fetchFixedKarma($canonicalTerm);
        if ($fixedKarma) {
            $message = $nick . ': ' . $term . ' ' . $fixedKarma;
            $this->doPrivmsg($source, $message);
            return;
        }

        $karma = $this->fetchKarma($canonicalTerm);

        $message = $nick . ': ';

        if ($term == 'me') {
            $message .= 'You have';
        } else {
            $message .= $term . ' has';
        }

        $message .= ' ';

        if ($karma) {
            $message .= 'karma of ' . $karma;
        } else {
            $message .= 'neutral karma';
        }

        $message .= '.';

        $this->doPrivmsg($source, $message);
    }

    /**
     * Resets the karma for a term to 0.
     *
     * @param string $term Term for which to reset the karma rating
     *
     * @return void
     */
    public function onCommandReincarnate($term)
    {
        $data = array(
            ':term' => $term,
            ':karma' => 0
        );
        $this->updateKarma->execute($data);
    }

    /**
     * Compares the karma between two terms. Optionally increases/decreases
     * the karma of either term.
     *
     * @param string $term0  First term
     * @param string $term1  Second term
     * @param string $method Comparison method (< or >)
     *
     * @return void
     */
    protected function compareKarma($term0, $term1, $method)
    {
        $event = $this->getEvent();
        $nick = $event->getNick();
        $source = $event->getSource();

        $canonicalTerm0 = $this->getCanonicalTerm($term0);
        $canonicalTerm1 = $this->getCanonicalTerm($term1);

        $fixedKarma0 = $this->fetchFixedKarma($canonicalTerm0);
        $fixedKarma1 = $this->fetchFixedKarma($canonicalTerm1);

        if ($fixedKarma0 || $fixedKarma1) {
            return;
        }

        if ($canonicalTerm0 == 'everything') {
            $change = $method == '<' ? '++' : '--';
            $karma0 = 0;
            $karma1 = $this->modifyKarma($canonicalTerm1, $change);
        } elseif ($canonicalTerm1 == 'everything') {
            $change = $method == '<' ? '--' : '++';
            $karma0 = $this->modifyKarma($canonicalTerm0, $change);
            $karma1 = 0;
        } else {
            $karma0 = $this->fetchKarma($canonicalTerm0);
            $karma1 = $this->fetchKarma($canonicalTerm1);
        }

        // Combining the first and second branches here causes an odd
        // single-line lapse in code coverage, but the lapse disappears if
        // they're separated
        if ($method == '<' && $karma0 < $karma1) {
            $replies = $this->fetchPositiveAnswer;
        } elseif ($method == '>' && $karma0 > $karma1) {
            $replies = $this->fetchPositiveAnswer;
        } else {
            $replies = $this->fetchNegativeAnswer;
        }
        $replies->execute();
        $reply = $replies->fetchColumn();

        if (max($karma0, $karma1) == $karma1) {
            list($canonicalTerm0, $canonicalTerm1) =
                array($canonicalTerm1, $canonicalTerm0);
        }

        $message = str_replace(
            array('%owner%','%owned%'),
            array($canonicalTerm0, $canonicalTerm1),
            $reply
        );

        $this->doPrivmsg($source, $message);
    }

    /**
     * Modifes a term's karma.
     *
     * @param string $term   Term to modify
     * @param string $action Karma action (either ++ or --)
     *
     * @return int Modified karma rating
     */
    protected function modifyKarma($term, $action)
    {
        $karma = $this->fetchKarma($term);
        if ($karma !== false) {
            $statement = $this->updateKarma;
        } else {
            $statement = $this->insertKarma;
        }

        $karma += ($action == '++') ? 1 : -1;

        $args = array(
            ':term'  => $term,
            ':karma' => $karma
        );
        $statement->execute($args);

        return $karma;
    }

    /**
     * Returns the karma rating for a specified term for which the karma
     * rating can be modified.
     *
     * @param string $term Term for which to fetch the corresponding karma
     *        rating
     *
     * @return integer|boolean Integer value denoting the term's karma or
     *         FALSE if there is the specified term has no associated karma
     *         rating
     */
    protected function fetchKarma($term)
    {
        $this->fetchKarma->execute(array(':term' => $term));
        $result = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);

        if ($result === false) {
            return false;
        }

        return (int) $result['karma'];
    }

    /**
     * Returns a phrase describing the karma rating for a specified term for
     * which the karma rating is fixed.
     *
     * @param string $term Term for which to fetch the corresponding karma
     *        rating
     *
     * @return string Phrase describing the karma rating, which may be append
     *         to the term to form a complete response
     */
    protected function fetchFixedKarma($term)
    {
        $this->fetchFixedKarma->execute(array(':term' => $term));
        $result = $this->fetchFixedKarma->fetch(PDO::FETCH_ASSOC);

        if ($result === false) {
            return false;
        }

        return $result['karma'];
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Lart
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Lart
 */

/**
 * Accepts terms and corresponding definitions for storage to a local data
 * source and performs and returns the result of lookups for term definitions
 * as they are requested.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Lart
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Lart
 * @uses     Phergie_Plugin_Command pear.phergie.org
 * @uses     extension PDO
 * @uses     extension pdo_sqlite
 */
class Phergie_Plugin_Lart extends Phergie_Plugin_Abstract
{
    /**
     * PDO instance for the database
     *
     * @var PDO
     */
    protected $db;

    /**
     * Prepared statement for inserting a new definition
     *
     * @var PDOStatement
     */
    protected $save;

    /**
     * Prepared statement for deleting the definition for a given term
     *
     * @var PDOStatement
     */
    protected $delete;

    /**
     * Prepared statement for searching for a definition for which the term
     * matches as a regular expression against a given search string
     *
     * @var PDOStatement
     */
    protected $process;

    /**
     * Prepared statement for searching for a definition by its exact term
     *
     * @var PDOStatement
     */
    protected $select;

    /**
     * Checks for dependencies and initializes the database.
     *
     * @return void
     */
    public function onLoad()
    {
        if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
            $this->fail('PDO and pdo_sqlite extensions must be installed');
        }

        $this->plugins->getPlugin('Command');

        $dir = dirname(__FILE__) . '/' . $this->getName();
        $path = $dir . '/lart.db';
        $exists = file_exists($path);
        if (!$exists) {
            mkdir($dir);
        }

        try {
            $this->db = new PDO('sqlite:' . $path);
        } catch (PDO_Exception $e) {
            throw new Phergie_Plugin_Exception($e->getMessage());
        }

        $this->db->sqliteCreateFunction('preg_match', 'preg_match');

        if (!$exists) {
            $this->db->exec('
                CREATE TABLE lart (
                    name VARCHAR(255),
                    definition TEXT,
                    hostmask VARCHAR(50),
                    tstamp VARCHAR(19)
                )
            ');
            $this->db->exec('
                CREATE UNIQUE INDEX lart_name ON lart (name)
            ');
        }

        $this->save = $this->db->prepare('
            REPLACE INTO lart (name, definition, hostmask, tstamp)
            VALUES (:name, :definition, :hostmask, :tstamp)
        ');

        $this->process = $this->db->prepare('
            SELECT *
            FROM lart
            WHERE preg_match(name, :name)
        ');

        $this->select = $this->db->prepare('
            SELECT *
            FROM lart
            WHERE name = :name
        ');

        $this->delete = $this->db->prepare('
            DELETE FROM lart
            WHERE name = :name
        ');
    }

    /**
     * Retrieves the definition for a given term if it exists.
     *
     * @param string $term Term to search for
     *
     * @return mixed String containing the definition or FALSE if no definition
     *               exists
     */
    protected function getLart($term)
    {
        $this->process->execute(array(':name' => $term));
        $row = $this->process->fetchObject();
        if ($row === false) {
            return false;
        }
        preg_match($row->name, $term, $match);
        $definition = preg_replace(
            "/(?:\\\\|\\$)([0-9]+)/e",
            '$match[\1]',
            $row->definition
        );
        $event = $this->getEvent();
        $definition = str_replace(
            array('$source', '$nick'),
            array($event->getSource(), $event->getNick()),
            $definition
        );
        return $definition;
    }

    /**
     * Deletes a given definition.
     *
     * @param string $term Term for which the definition should be deleted
     *
     * @return boolean TRUE if the definition was found and deleted, FALSE
     *         otherwise
     */
    protected function deleteLart($term)
    {
        $this->delete->execute(array(':name' => $term));
        return ($this->delete->rowCount() > 0);
    }

    /**
     * Saves a given definition.
     *
     * @param string $term       Term to trigger a response containing the
     *        corresponding definition, may be a regular expression
     * @param string $definition Definition corresponding to the term
     *
     * @return boolean TRUE if the definition was saved successfully, FALSE
     *         otherwise
     */
    protected function saveLart($term, $definition)
    {
        $data = array(
            ':name' => $term,
            ':definition' => $definition,
            ':hostmask' => (string) $this->getEvent()->getHostmask(),
            ':tstamp' => time()
        );
        $this->save->execute($data);
        return ($this->save->rowCount() > 0);
    }

    /**
     * Returns information about a definition.
     *
     * @param string $term Term about which to return information
     *
     * @return void
     */
    public function onCommandLartinfo($term)
    {
        $this->select->execute(array(':name' => $term));
        $row = $this->select->fetchObject();
        $msg = $this->getEvent()->getNick() . ': ';
        if (!$row) {
            $msg .= 'Lart not found';
        } else {
            $msg .= 'Term: ' . $row->name
                . ', Definition: ' . $row->definition
                . ', User: ' . $row->hostmask
                . ', Added: ' . date('n/j/y g:i A', $row->tstamp);
        }
        $this->doNotice($this->getEvent()->getSource(), $msg);
    }

    /**
     * Creates a new definition.
     *
     * @param string $term       Term to add
     * @param string $definition Definition to add
     *
     * @return void
     */
    public function onCommandAddlart($term, $definition)
    {
        $result = $this->saveLart($term, $definition);
        if ($result) {
            $msg = 'Lart saved successfully';
        } else {
            $msg = 'Lart could not be saved';
        }
        $this->doNotice($this->getEvent()->getSource(), $msg);
    }

    /**
     * Removes an existing definition.
     *
     * @param string $term Term for which the definition should be removed
     *
     * @return void
     */
    public function onCommandDeletelart($term)
    {
        $source = $this->getEvent()->getSource();
        if ($this->deleteLart($term)) {
            $msg = 'Lart deleted successfully';
        } else {
            $msg = 'Lart not found';
        }
        $this->doNotice($source, $msg);
    }

    /**
     * Processes definition triggers in the text of the current event.
     *
     * @return void
     */
    protected function processLart()
    {
        $lart = $this->getLart($this->getEvent()->getText());
        if ($lart) {
            if (strpos($lart, '/me') === 0) {
                $lart = substr($lart, 4);
                $method = 'doAction';
            } else {
                $method = 'doPrivmsg';
            }
            $this->$method($this->getEvent()->getSource(), $lart);
        }
    }

    /**
     * Processes definition triggers in messages.
     *
     * @return void
     */
    public function onPrivmsg()
    {
        $this->processLart();
    }

    /**
     * Processes definition triggers in CTCP actions.
     *
     * @return void
     */
    public function onAction()
    {
        $this->processLart();
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Message
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Message
 */

/**
 * Generalized plugin providing utility methods for
 * prefix and bot named based message extraction.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Message
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Message
 */
class Phergie_Plugin_Message extends Phergie_Plugin_Abstract
{

    /**
     * Check whether a message is specifically targeted at the bot.
     * This is the case when the message starts with the bot's name
     * followed by [,:>] or when it is a private message.
     *
     * @return boolean true when the message is specifically targeted at the bot,
     *                 false otherwise.
     */
    public function isTargetedMessage()
    {
        $event = $this->getEvent();

        $self = preg_quote($this->connection->getNick());

        $targetPattern = <<<REGEX
        {^
        \s*{$self}\s*[:>,].* # expect the bots name, followed by a [:>,]
        $}ix
REGEX;

        return !$event->isInChannel()
            || preg_match($targetPattern, $event->getText()) > 0;
    }

    /**
     * Allow for prefix and bot name aware extraction of a message
     *
     * @return string|bool $message The message, which is possibly targeted at the
     *                              bot or false if a prefix requirement failed
     */
    public function getMessage()
    {
        $event = $this->getEvent();

        $prefix = preg_quote($this->getConfig('command.prefix'));
        $self = preg_quote($this->connection->getNick());
        $message = $event->getText();

        // $prefixPattern matches : Phergie, do command <parameters>
        // where $prefix = 'do'   : do command <parameters>
        //                        : Phergie, command <parameters>
        $prefixPattern = <<<REGEX
        {^
        (?:
        	\s*{$self}\s*[:>,]\s* # start with bot name
			(?:{$prefix})?        # which is optionally followed by the prefix
        |
        	\s*{$prefix}          # or start with the prefix
        )
        \s*(.*)                   # always end with the message
        $}ix
REGEX;

        // $noPrefixPattern matches : Phergie, command <parameters>
        //                          : command <parameters>
        $noPrefixPattern = <<<REGEX
        {^
        \s*(?:{$self}\s*[:>,]\s*)? # optionally start with the bot name
        (.*?)                      # always end with the message
        $}ix
REGEX;

        $pattern = $noPrefixPattern;

        // If a prefix is set, force it as a requirement
        if ($prefix && $event->isInChannel()) {
            $pattern = $prefixPattern;
        }

        $match = null;

        if (!preg_match($pattern, $message, $match)) {
            return false;
        }

        return $match[1];
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_NickServ
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_NickServ
 */

/**
 * Intercepts and responds to messages from the NickServ agent requesting that
 * the bot authenticate its identify.
 *
 * The password configuration setting should contain the password registered
 * with NickServ for the nick used by the bot.
 *
 * @category Phergie
 * @package  Phergie_Plugin_NickServ
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_NickServ
 * @uses     Phergie_Plugin_Command pear.phergie.org
 */
class Phergie_Plugin_NickServ extends Phergie_Plugin_Abstract
{
    /**
     * Nick of the NickServ bot
     *
     * @var string
     */
    protected $botNick;

    /**
    * Identify message
    */
    protected $identifyMessage;

    /**
     * Checks for dependencies and required configuration settings.
     *
     * @return void
     */
    public function onLoad()
    {
        $this->getPluginHandler()->getPlugin('Command');

        // Get the name of the NickServ bot, defaults to NickServ
        $this->botNick = $this->getConfig('nickserv.botnick', 'NickServ');

        // Get the identify message
        $this->identifyMessage = $this->getConfig(
            'nickserv.identify_message',
            '/This nickname is registered./'
        );
    }

    /**
     * Checks for a notice from NickServ and responds accordingly if it is an
     * authentication request or a notice that a ghost connection has been
     * killed.
     *
     * @return void
     */
    public function onNotice()
    {
        $event = $this->event;
        if (strtolower($event->getNick()) == strtolower($this->botNick)) {
            $message = $event->getArgument(1);
            $nick = $this->connection->getNick();
            if (preg_match($this->identifyMessage, $message)) {
                $password = $this->config['nickserv.password'];
                if (!empty($password)) {
                    $this->doPrivmsg($this->botNick, 'IDENTIFY ' . $password);
                }
                unset($password);
            } elseif (preg_match('/^.*' . $nick . '.* has been killed/', $message)) {
                $this->doNick($nick);
            }
        }
    }

    /**
     * Checks to see if the original nick has quit; if so, take the name back.
     *
     * @return void
     */
    public function onQuit()
    {
        $eventNick = $this->event->getNick();
        $nick = $this->connection->getNick();
        if ($eventNick == $nick) {
            $this->doNick($nick);
        }
    }

    /**
     * Changes the in-memory configuration setting for the bot nick if it is
     * successfully changed.
     *
     * @return void
     */
    public function onNick()
    {
        $event = $this->event;
        $connection = $this->connection;
        if ($event->getNick() == $connection->getNick()) {
            $connection->setNick($event->getArgument(0));
        }
    }

    /**
     * Provides a command to terminate ghost connections.
     *
     * @return void
     */
    public function onCommandGhostbust()
    {
        $event = $this->event;
        $user = $event->getNick();
        $conn = $this->connection;
        $nick = $conn->getNick();

        if ($nick != $this->config['connections'][$conn->getHost()]['nick']) {
            $password = $this->config['nickserv.password'];
            if (!empty($password)) {
                $this->doPrivmsg(
                    $this->event->getSource(),
                    $user . ': Attempting to ghost ' . $nick .'.'
                );
                $this->doPrivmsg(
                    $this->botNick,
                    'GHOST ' . $nick . ' ' . $password,
                    true
                );
            }
        }
    }

    /**
     * Automatically send the GHOST command if the bot's nick is in use.
     *
     * @return void
     */
    public function onResponse()
    {
        if ($this->event->getCode() == Phergie_Event_Response::ERR_NICKNAMEINUSE) {
            $password = $this->config['nickserv.password'];
            if (!empty($password)) {
                $this->doPrivmsg(
                    $this->botNick,
                    'GHOST ' . $this->connection->getNick() . ' ' . $password
                );
            }
        }
    }

    /**
     * Handle the server sending a KILL request.
     *
     * @return void
     */
    public function onKill()
    {
        $this->doQuit($this->event->getArgument(1));
    }
}


code:
<?php
/**
 * Phergie
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie
 * @package   Phergie_Plugin_Part
 * @author    Phergie Development Team <team@phergie.org>
 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
 * @license   http://phergie.org/license New BSD License
 * @link      http://pear.phergie.org/package/Phergie_Plugin_Part
 */

/**
 * Parts a specified channel on command from a user.
 *
 * @category Phergie
 * @package  Phergie_Plugin_Part
 * @author   Phergie Development Team <team@phergie.org>
 * @license  http://phergie.org/license New BSD License
 * @link     http://pear.phergie.org/package/Phergie_Plugin_Part
 * @uses     Phergie_Plugin_Command pear.phergie.org
 */
class Phergie_Plugin_Part extends Phergie_Plugin_Abstract
{
    /**
     * Checks for dependencies.
     *
     * @return void
     */
    public function onLoad()
    {
        $this->getPluginHandler()->getPlugin('Command');
    }

    /**
     * Parts a channel.
     *
     * @param string $channels Comma-delimited list of channels to leave
     *
     * @return void
     */
    public function onCommandPart($channels)
    {
        $this->doPart($channels);
    }
}