<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Autoloader for Phergie classes.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
*/
public function __construct()
{
- $path = dirname(__FILE__);
+ $path = realpath(dirname(__FILE__) . '/..');
$includePath = get_include_path();
- $includePathList = explode(PATH_SEPARATOR, $includePath);
+ $includePathList = explode(PATH_SEPARATOR, $includePath);
if (!in_array($path, $includePathList)) {
self::addPath($path);
}
*/
public function load($class)
{
- if (substr($class, 0, 8) == 'Phergie_') {
- $class = substr($class, 8);
- }
include str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Composite class for other components to represent the bot.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
protected $config;
/**
- * Current connection handler instance
+ * Current connection handler instance
*
- * @var Phergie_Connection_Handler
+ * @var Phergie_Connection_Handler
*/
protected $connections;
protected $processor;
/**
- * Returns a driver instance, creating one of the default class if
+ * Returns a driver instance, creating one of the default class if
* none has been set.
*
* @return Phergie_Driver_Abstract
}
/**
- * Returns the entire configuration in use or the value of a specific
+ * Returns the entire configuration in use or the value of a specific
* configuration setting.
*
- * @param string $index Optional index of a specific configuration
+ * @param string $index Optional index of a specific configuration
* setting for which the corresponding value should be returned
* @param mixed $default Value to return if no match is found for $index
*
- * @return mixed Value corresponding to $index or the entire
+ * @return mixed Value corresponding to $index or the entire
* configuration if $index is not specified
*/
public function getConfig($index = null, $default = null)
}
/**
- * Returns a connection handler instance, creating it if it does not
+ * Returns a connection handler instance, creating it if it does not
* already exist and using a default class if none has been set.
*
* @return Phergie_Connection_Handler
}
/**
- * Returns an end-user interface instance, creating it if it does not
+ * Returns an end-user interface instance, creating it if it does not
* already exist and using a default class if none has been set.
*
* @return Phergie_Ui_Abstract
}
/**
- * Returns a processer instance, creating one if none exists.
+ * Returns a processer instance, creating one if none exists.
*
* @return Phergie_Process_Abstract
*/
{
if (empty($this->processor)) {
$class = 'Phergie_Process_Standard';
-
+
$type = $this->getConfig('processor');
if (!empty($type)) {
$class = 'Phergie_Process_' . ucfirst($type);
}
$this->processor = new $class(
- $this,
+ $this,
$this->getConfig('processor.options', array())
);
}
$config = $this->getConfig();
$plugins = $this->getPluginHandler();
$ui = $this->getUi();
-
+
$plugins->setAutoload($config['plugins.autoload']);
foreach ($config['plugins'] as $name) {
try {
}
/**
- * Establishes server connections and initiates an execution loop to
+ * Establishes server connections and initiates an execution loop to
* continuously receive and process events.
*
- * @return Phergie_Bot Provides a fluent interface
+ * @return Phergie_Bot Provides a fluent interface
*/
public function run()
{
set_time_limit(0);
-
+
$timezone = $this->getConfig('timezone', 'UTC');
date_default_timezone_set($timezone);
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Data structure for connection metadata.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
protected $host;
/**
- * Port on which the client will connect, defaults to the standard IRC
+ * Port on which the client will connect, defaults to the standard IRC
* port
*
* @var int
protected $port;
/**
- * Transport for the connection, defaults to tcp but can be set to ssl
+ * Transport for the connection, defaults to tcp but can be set to ssl
* or variations thereof to connect over SSL
*
- * @var string
+ * @var string
*/
protected $transport;
+ /**
+ * Encoding method for the connection, defaults to ISO-8859-1 but can
+ * be set to UTF8 if necessary
+ *
+ * @var strng
+ */
+ protected $encoding;
+
/**
* Nick that the client will use
*
/**
* Constructor to initialize instance properties.
*
- * @param array $options Optional associative array of property values
+ * @param array $options Optional associative array of property values
* to initialize
*
* @return void
public function __construct(array $options = array())
{
$this->transport = 'tcp';
+ $this->encoding = 'ISO-8859-1';
+ // @note this may need changed to something different, for broader support.
+ // @note also may need to make use of http://us.php.net/manual/en/function.stream-encoding.php
$this->setOptions($options);
}
);
}
}
-
+
/**
* Returns a hostmask that uniquely identifies the connection.
*
);
}
- return $this->hostmask;
+ return $this->hostmask;
}
/**
*
* @param string $host Hostname
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setHost($host)
{
return $this;
}
-
+
/**
- * Returns the host to which the client will connect if it is set or
+ * Returns the host to which the client will connect if it is set or
* emits an error if it is not set.
*
* @return string
*
* @param int $port Port
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setPort($port)
{
}
/**
- * Sets the transport for the connection to use.
+ * Sets the transport for the connection to use.
*
- * @param string $transport Transport (ex: tcp, ssl, etc.)
+ * @param string $transport Transport (ex: tcp, ssl, etc.)
*
* @return Phergie_Connection Provides a fluent interface
*/
}
/**
- * Returns the transport in use by the connection.
+ * Returns the transport in use by the connection.
*
- * @return string Transport (ex: tcp, ssl, etc.)
+ * @return string Transport (ex: tcp, ssl, etc.)
*/
public function getTransport()
{
return $this->transport;
}
+ /**
+ * Sets the encoding for the connection to use.
+ *
+ * @param string $encoding Encoding to use (ex: ASCII, ISO-8859-1, UTF8, etc.)
+ *
+ * @return Phergie_Connection Provides a fluent interface
+ */
+ public function setEncoding($encoding)
+ {
+ $this->encoding = (string) $encoding;
+
+ if (!in_array($this->encoding, mb_list_encodings())) {
+ throw new Phergie_Connection_Exception(
+ 'Encoding ' . $this->encoding . ' is not supported',
+ Phergie_Connection_Exception::ENCODING_NOT_SUPPORTED
+ );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the encoding in use by the connection.
+ *
+ * @return string Encoding (ex: ASCII, ISO-8859-1, UTF8, etc.)
+ */
+ public function getEncoding()
+ {
+ return $this->encoding;
+ }
+
/**
* Sets the nick that the client will use.
*
* @param string $nick Nickname
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setNick($nick)
{
*
* @param string $username Username
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setUsername($username)
{
*
* @param string $realname Real name
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setRealname($realname)
{
*
* @param string $password Password
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setPassword($password)
{
/**
* Sets multiple connection settings using an array.
*
- * @param array $options Associative array of setting names mapped to
+ * @param array $options Associative array of setting names mapped to
* corresponding values
*
- * @return Phergie_Connection Provides a fluent interface
+ * @return Phergie_Connection Provides a fluent interface
*/
public function setOptions(array $options)
{
--- /dev/null
+<?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
+ */
+
+/**
+ * Exceptions related to handling databases for 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_Db_Exception extends Phergie_Exception
+{
+ /**
+ * Error indicating that a directory needed to support database
+ * functionality was unable to be created.
+ */
+ const ERR_UNABLE_TO_CREATE_DIRECTORY = 1;
+}
--- /dev/null
+<?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_Command
+ */
+
+/**
+ * Database management class. Provides a base API for managing databases
+ * within
+ *
+ * @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_Db_Manager
+ */
+abstract class Phergie_Db_Manager
+{
+ /**
+ * Returns a connection to the database.
+ *
+ * @return object
+ */
+ public abstract function getDb();
+
+ /**
+ * Checks if a table/collection exists within the database.
+ *
+ * @param string $table Table/collection name to check for
+ *
+ * @return boolean
+ */
+ public abstract function hasTable($table);
+}
--- /dev/null
+<?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
+ */
+
+/**
+ * Provides basic management for SQLite databases
+ *
+ * @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_Db_Sqlite extends Phergie_Db_Manager
+{
+ /**
+ * Database connection
+ *
+ * @var PDO
+ */
+ protected $db;
+
+ /**
+ * Database file path
+ *
+ * @var string
+ */
+ protected $dbFile;
+
+ /**
+ * Allows setting of the database file path when creating the class.
+ *
+ * @param string $dbFile database file path (optional)
+ *
+ * @return void
+ */
+ public function __construct($dbFile = null)
+ {
+ if ($dbFile != null) {
+ $this->setDbFile($dbFile);
+ }
+ }
+
+ /**
+ * Sets the database file path.
+ *
+ * @param string $dbFile SQLite database file path
+ *
+ * @return null
+ */
+ public function setDbFile($dbFile)
+ {
+ if (is_string($dbFile) && !empty($dbFile)) {
+ $this->dbFile = $dbFile;
+ }
+ }
+
+ /**
+ * Returns a configured database connection.
+ *
+ * @return PDO
+ */
+ public function getDb()
+ {
+ if (!empty($this->db)) {
+ return $this->db;
+ }
+
+ $dir = dirname($this->dbFile);
+ if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
+ throw new Phergie_Db_Exception(
+ 'Unable to create directory',
+ Phergie_Db_Exception::ERR_UNABLE_TO_CREATE_DIRECTORY
+ );
+ }
+
+ $this->db = new PDO('sqlite:' . $this->dbFile);
+
+ return $this->db;
+ }
+
+
+ /**
+ * Returns whether a given table exists in the database.
+ *
+ * @param string $table Name of the table to check for
+ *
+ * @return boolean TRUE if the table exists, FALSE otherwise
+ */
+ public function hasTable($table)
+ {
+ $db = $this->getDb();
+
+ return (bool) $db->query(
+ 'SELECT COUNT(*) FROM sqlite_master WHERE name = '
+ . $db->quote($table)
+ )->fetchColumn();
+ }
+}
* Error indicating that an attempt to initiate a connection failed
*/
const ERR_CONNECTION_ATTEMPT_FAILED = 3;
+
+ /**
+ * Error indicating that an attempt to send data via a connection failed
+ */
+ const ERR_CONNECTION_WRITE_FAILED = 4;
}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
*/
/**
- * Driver that uses the sockets wrapper of the streams extension for
- * communicating with the server and handles formatting and parsing of
+ * Driver that uses the sockets wrapper of the streams extension for
+ * communicating with the server and handles formatting and parsing of
* events using PHP.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
protected $socket;
/**
- * Amount of time in seconds to wait to receive an event each time the
+ * Amount of time in seconds to wait to receive an event each time the
* socket is polled
*
- * @var float
+ * @var float
*/
protected $timeout = 0.1;
/**
- * Handles construction of command strings and their transmission to the
+ * Handles construction of command strings and their transmission to the
* server.
*
* @param string $command Command to send
- * @param string|array $args Optional string or array of sequential
+ * @param string|array $args Optional string or array of sequential
* arguments
*
- * @return string Command string that was sent
+ * @return string Command string that was sent
* @throws Phergie_Driver_Exception
*/
protected function send($command, $args = '')
{
+ $connection = $this->getConnection();
+ $encoding = $connection->getEncoding();
+
// Require an open socket connection to continue
if (empty($this->socket)) {
throw new Phergie_Driver_Exception(
$end = count($args) - 1;
$args[$end] = ':' . $args[$end];
$args = implode(' ', $args);
+ } else {
+ $args = ':' . $args;
}
$buffer .= ' ' . $args;
}
// Transmit the command over the socket connection
- fwrite($this->socket, $buffer . "\r\n");
+ $attempts = $written = 0;
+ $temp = $buffer . "\r\n";
+ $is_multibyte = !substr($encoding, 0, 8) === 'ISO-8859' && $encoding !== 'ASCII' && $encoding !== 'CP1252';
+ $length = ($is_multibyte) ? mb_strlen($buffer, '8bit') : strlen($buffer);
+ while (true) {
+ $written += (int) fwrite($this->socket, $temp);
+ if ($written < $length) {
+ $temp = substr($temp, $written);
+ $attempts++;
+ if ($attempts == 3) {
+ throw new Phergie_Driver_Exception(
+ 'Unable to write to socket',
+ Phergie_Driver_Exception::ERR_CONNECTION_WRITE_FAILED
+ );
+ }
+ } else {
+ break;
+ }
+ }
// Return the command string that was transmitted
return $buffer;
}
/**
- * Overrides the parent class to set the currently active socket handler
+ * Overrides the parent class to set the currently active socket handler
* when the active connection is changed.
*
* @param Phergie_Connection $connection Active connection
/**
* Returns a list of hostmasks corresponding to sockets with data to read.
- *
+ *
* @param int $sec Length of time to wait for new data (seconds)
* @param int $usec Length of time to wait for new data (microseconds)
*
- * @return array List of hostmasks or an empty array if none were found
+ * @return array List of hostmasks or an empty array if none were found
* to have data to read
*/
public function getActiveReadSockets($sec = 0, $usec = 200000)
}
/**
- * Sets the amount of time to wait for a new event each time the socket
+ * Sets the amount of time to wait for a new event each time the socket
* is polled.
*
* @param float $timeout Amount of time in seconds
}
/**
- * Returns the amount of time to wait for a new event each time the
+ * Returns the amount of time to wait for a new event each time the
* socket is polled.
*
* @return float Amount of time in seconds
}
/**
- * Supporting method to parse event argument strings where the last
+ * Supporting method to parse event argument strings where the last
* argument may contain a colon.
*
* @param string $args Argument string to parse
/**
* Listens for an event on the current connection.
*
- * @return Phergie_Event_Interface|null Event instance if an event was
+ * @return Phergie_Event_Interface|null Event instance if an event was
* received, NULL otherwise
*/
public function getEvent()
// If the event could be from the server or a user...
// Parse the server hostname or user hostmask, command, and arguments
- list($prefix, $cmd, $args)
+ list($prefix, $cmd, $args)
= array_pad(explode(' ', ltrim($buffer, ':'), 3), 3, null);
if (strpos($prefix, '@') !== false) {
$hostmask = Phergie_Hostmask::fromString($prefix);
case 'oper':
case 'topic':
case 'mode':
- $args = $this->parseArguments($args);
+ $args = $this->parseArguments($args);
break;
case 'part':
case 'kill':
case 'invite':
- $args = $this->parseArguments($args, 2);
+ $args = $this->parseArguments($args, 2);
break;
case 'kick':
- $args = $this->parseArguments($args, 3);
+ $args = $this->parseArguments($args, 3);
break;
// Remove the target from responses
$this->send(
'USER',
array(
- $username,
- $hostname,
- $hostname,
+ $username,
+ $hostname,
+ $hostname,
$realname
)
);
- $this->send('NICK', $nick);
+ $this->send('NICK', $nick);
// Add the socket handler to the internal array for socket handlers
$this->sockets[(string) $connection->getHostmask()] = $this->socket;
/**
* Joins a channel.
*
- * @param string $channels Comma-delimited list of channels to join
+ * @param string $channels Comma-delimited list of channels to join
* @param string $keys Optional comma-delimited list of channel keys
*
* @return void
/**
* Leaves a channel.
*
- * @param string $channels Comma-delimited list of channels to leave
+ * @param string $channels Comma-delimited list of channels to leave
*
* @return void
*/
/**
* Sends a CTCP response to a user.
*
- * @param string $nick User nick
+ * @param string $nick User nick
* @param string $command Command to send
- * @param string|array $args String or array of sequential arguments
+ * @param string|array $args String or array of sequential arguments
* (optional)
*
* @return void
$buffer = rtrim(strtoupper($command) . ' ' . $args);
- $this->doNotice($nick, chr(1) . $buffer . chr(1));
+ $this->doNotice($nick, chr(1) . $buffer . chr(1));
}
/**
* Sends a CTCP TIME request to a user.
*
* @param string $nick User nick
- * @param string $time Time string to send for a response
+ * @param string $time Time string to send for a response
*
* @return void
*/
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
*/
/**
- * Handles events initiated by plugins.
+ * Handles events initiated by plugins.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* Adds an event to the queue.
*
* @param Phergie_Plugin_Abstract $plugin Plugin originating the event
- * @param string $type Event type, corresponding to a
+ * @param string $type Event type, corresponding to a
* Phergie_Event_Command::TYPE_* constant
* @param array $args Optional event arguments
*
* @return Phergie_Event_Handler Provides a fluent interface
*/
- public function addEvent(Phergie_Plugin_Abstract $plugin, $type,
+ public function addEvent(Phergie_Plugin_Abstract $plugin, $type,
array $args = array()
) {
if (!defined('Phergie_Event_Command::TYPE_' . strtoupper($type))) {
/**
* Replaces the current event queue with a given queue of events.
*
- * @param array $events Ordered list of objects of the class
+ * @param array $events Ordered list of objects of the class
* Phergie_Event_Command
*
* @return Phergie_Event_Handler Provides a fluent interface
/**
* Returns whether an event of the given type exists in the queue.
*
- * @param string $type Event type from Phergie_Event_Request::TYPE_*
+ * @param string $type Event type from Phergie_Event_Request::TYPE_*
* constants
*
- * @return bool TRUE if an event of the specified type exists in the
+ * @return bool TRUE if an event of the specified type exists in the
* queue, FALSE otherwise
*/
public function hasEventOfType($type)
/**
* Returns a list of events of a specified type.
*
- * @param string $type Event type from Phergie_Event_Request::TYPE_*
+ * @param string $type Event type from Phergie_Event_Request::TYPE_*
* constants
*
- * @return array Array containing event instances of the specified type
+ * @return array Array containing event instances of the specified type
* or an empty array if no such events were found
*/
public function getEventsOfType($type)
return $events;
}
+ /**
+ * Removes a single event from the event queue.
+ *
+ * @param Phergie_Event_Command $event Event to remove
+ *
+ * @return Phergie_Event_Handler Provides a fluent interface
+ */
+ public function removeEvent(Phergie_Event_Command $event)
+ {
+ $key = array_search($event, $this->events);
+ if ($key !== false) {
+ unset($this->events[$key]);
+ }
+ return $this;
+ }
+
/**
* Returns an iterator for the current event queue.
*
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Autonomous event originating from a user or the server.
*
- * @category Phergie
+ * @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
* @link http://www.irchelp.org/irchelp/rfc/chapter4.html
*/
-class Phergie_Event_Request
- extends Phergie_Event_Abstract
+class Phergie_Event_Request
+ extends Phergie_Event_Abstract
implements ArrayAccess
{
/**
return $this;
}
+ /**
+ * Sets the value of a single argument for the request.
+ *
+ * @param mixed $argument Integer position (starting from 0) or the
+ * equivalent string name of the argument from self::$map
+ * @param string $value Value to assign to the argument
+ *
+ * @return Phergie_Event_Request Provides a fluent interface
+ */
+ public function setArgument($argument, $value)
+ {
+ $argument = $this->resolveArgument($argument);
+ if ($argument !== null) {
+ $this->arguments[$argument] = (string) $value;
+ }
+ return $this;
+ }
+
/**
* Returns the arguments for the request.
*
* @param mixed $argument Integer position (starting from 0) or the
* equivalent string name of the argument from self::$map
*
- * @return int|null Integer position of the argument or NULL if no
+ * @return int|null Integer position of the argument or NULL if no
* corresponding argument was found
*/
protected function resolveArgument($argument)
{
if (isset($this->arguments[$argument])) {
- return $argument;
+ return $argument;
} else {
$argument = strtolower($argument);
if (isset(self::$map[$this->type][$argument])
public function getArgument($argument)
{
$argument = $this->resolveArgument($argument);
- if ($argument !== null) {
+ if ($argument !== null) {
return $this->arguments[$argument];
}
return null;
}
/**
- * Returns the channel name if the event occurred in a channel or the
- * user nick if the event was a private message directed at the bot by a
- * user.
+ * Returns the channel name if the event occurred in a channel or the
+ * user nick if the event was a private message directed at the bot by a
+ * user.
*
* @return string
*/
*
* @param string|int $offset Argument name or position beginning from 0
*
- * @return bool TRUE if the argument has a value, FALSE otherwise
+ * @return bool TRUE if the argument has a value, FALSE otherwise
* @see ArrayAccess::offsetExists()
*/
public function offsetExists($offset)
public function offsetSet($offset, $value)
{
$offset = $this->resolveArgument($offset);
- if ($offset !== null) {
+ if ($offset !== null) {
$this->arguments[$offset] = $value;
}
}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
* Base class for plugins to provide event handler stubs and commonly needed
* functionality.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
*/
protected $event;
+ /**
+ * Plugin short name
+ *
+ * @var string
+ */
+ protected $name;
+
/**
* Returns the short name for the plugin based on its class name.
*
*/
public function getName()
{
- return substr(strrchr(get_class($this), '_'), 1);
+ if (empty($this->name)) {
+ $this->name = substr(strrchr(get_class($this), '_'), 1);
+ }
+ return $this->name;
+ }
+
+ /**
+ * Sets the short name for the plugin.
+ *
+ * @param string $name Plugin short name
+ *
+ * @return Phergie_Plugin_Abstract Provides a fluent interface
+ */
+ public function setName($name)
+ {
+ $this->name = (string) $name;
+ return $this;
}
/**
- * Indicates that the plugin failed to load due to an unsatisfied
+ * Indicates that the plugin failed to load due to an unsatisfied
* runtime requirement, such as a missing dependency.
*
- * @param string $message Error message to provide more information
+ * @param string $message Error message to provide more information
* about the reason for the failure
*
* @return Phergie_Plugin_Abstract Provides a fluent interface
}
/**
- * Returns the current configuration handler or the value of a single
+ * Returns the current configuration handler or the value of a single
* setting from it.
*
- * @param string $name Optional name of a setting for which the value
+ * @param string $name Optional name of a setting for which the value
* should be returned instead of the entire configuration handler
- * @param mixed $default Optional default value to return if no value
+ * @param mixed $default Optional default value to return if no value
* is set for the setting indicated by $name
*
- * @return Phergie_Config|mixed Configuration handler or value of the
+ * @return Phergie_Config|mixed Configuration handler or value of the
* setting specified by $name
- * @throws Phergie_Plugin_Exception No configuration handler has been set
+ * @throws Phergie_Plugin_Exception No configuration handler has been set
*/
public function getConfig($name = null, $default = null)
{
* Returns the current plugin handler.
*
* @return Phergie_Plugin_Handler
- * @throws Phergie_Plugin_Exception No plugin handler has been set
+ * @throws Phergie_Plugin_Exception No plugin handler has been set
*/
public function getPluginHandler()
{
* Returns the current event handler.
*
* @return Phergie_Event_Handler
- * @throws Phergie_Plugin_Exception No event handler has been set
+ * @throws Phergie_Plugin_Exception No event handler has been set
*/
public function getEventHandler()
{
* Returns the current event connection.
*
* @return Phergie_Connection
- * @throws Phergie_Plugin_Exception No connection has been set
+ * @throws Phergie_Plugin_Exception No connection has been set
*/
public function getConnection()
{
}
/**
- * Handler for when the plugin is initially loaded - useful for checking
- * runtime dependencies or performing any setup necessary for the plugin
+ * Handler for when the plugin is initially loaded - useful for checking
+ * runtime dependencies or performing any setup necessary for the plugin
* to function properly such as initializing a database.
*
* @return void
}
/**
- * Handler for each tick, a single iteration of the continuous loop
- * executed by the bot to receive, handle, and send events - useful for
+ * Handler for each tick, a single iteration of the continuous loop
+ * executed by the bot to receive, handle, and send events - useful for
* repeated execution of tasks on a time interval.
*
* @return void
}
/**
- * Handler for when any event is received but has not yet been dispatched
+ * Handler for when any event is received but has not yet been dispatched
* to the plugin handler method specific to its event type.
*
- * @return bool|null|void FALSE to short-circuit further event
+ * @return bool|null|void FALSE to short-circuit further event
* processing, TRUE or NULL otherwise
*/
public function preEvent()
}
/**
- * Handler for after plugin processing of an event has concluded but
- * before any events triggered in response by plugins are sent to the
+ * Handler for after plugin processing of an event has concluded but
+ * before any events triggered in response by plugins are sent to the
* server - useful for modifying outgoing events before they are sent.
*
* @return void
}
/**
- * Handler for after any events triggered by plugins in response to a
- * received event are sent to the server.
+ * Handler for after any events triggered by plugins in response to a
+ * received event are sent to the server.
*
* @return void
*/
}
/**
- * Handler for when the bot receives a ping event from a server, at
- * which point it is expected to respond with a pong request within
+ * Handler for when the bot receives a ping event from a server, at
+ * which point it is expected to respond with a pong request within
* a short period else the server may terminate its connection.
*
* @return void
- * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_6_2
+ * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_6_2
*/
public function onPing()
{
}
/**
- * Handler for when the bot receives a CTCP request of an unknown type.
+ * Handler for when the bot receives a CTCP request of an unknown type.
*
* @return void
* @link http://www.invlogic.com/irc/ctcp.html
}
/**
- * Handler for when a reply is received for a CTCP PING request sent by
+ * Handler for when a reply is received for a CTCP PING request sent by
* the bot.
*
* @return void
}
/**
- * Handler for when a reply is received for a CTCP TIME request sent by
+ * Handler for when a reply is received for a CTCP TIME request sent by
* the bot.
*
* @return void
}
/**
- * Handler for when a reply is received for a CTCP VERSION request sent
+ * Handler for when a reply is received for a CTCP VERSION request sent
* by the bot.
*
* @return void
}
/**
- * Handler for when a reply received for a CTCP request of an unknown
+ * Handler for when a reply received for a CTCP request of an unknown
* type.
*
* @return void
}
/**
- * Handler for when the bot receives an invitation to join a channel.
+ * Handler for when the bot receives an invitation to join a channel.
*
* @return void
* @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_7
}
/**
- * Handler for when a server response is received to a command issued by
+ * Handler for when a server response is received to a command issued by
* the bot.
*
* @return void
--- /dev/null
+<?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_Censor
+ * @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_Censor
+ */
+
+/**
+ * Facilitates censoring of event content or discardment of events
+ * containing potentially offensive phrases depending on the value of the
+ * configuration setting censor.mode ('off', 'censor', 'discard'). Also
+ * provides access to a web service for detecting censored words so that
+ * other plugins may optionally integrate and adjust behavior accordingly to
+ * prevent discardment of events.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_Censor
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_Censor
+ * @uses extension soap
+ */
+class Phergie_Plugin_Censor extends Phergie_Plugin_Abstract
+{
+ /**
+ * SOAP client to interact with the CDYNE Profanity Filter API
+ *
+ * @var SoapClient
+ */
+ protected $soap;
+
+ /**
+ * Checks for dependencies.
+ *
+ * @return void
+ */
+ public function onLoad()
+ {
+ if (!extension_loaded('soap')) {
+ $this->fail('The PHP soap extension is required');
+ }
+
+ if (!in_array($this->config['censor.mode'], array('censor', 'discard'))) {
+ $this->plugins->removePlugin($this);
+ }
+ }
+
+ /**
+ * Returns a "clean" version of a given string.
+ *
+ * @param string $string String to clean
+ *
+ * @return string Cleaned string
+ */
+ public function cleanString($string)
+ {
+ if (empty($this->soap)) {
+ $this->soap = new SoapClient('http://ws.cdyne.com/ProfanityWS/Profanity.asmx?wsdl');
+ }
+ $params = array('Text' => $string);
+ $response = $this->soap->SimpleProfanityFilter($params);
+ return $response->SimpleProfanityFilterResult->CleanText;
+ }
+
+ /**
+ * Processes events before they are dispatched and either censors their
+ * content or discards them if they contain potentially offensive
+ * content.
+ *
+ * @return void
+ */
+ public function preDispatch()
+ {
+ $events = $this->events->getEvents();
+
+ foreach ($events as $event) {
+ switch ($event->getType()) {
+ case Phergie_Event_Request::TYPE_PRIVMSG:
+ case Phergie_Event_Request::TYPE_ACTION:
+ case Phergie_Event_Request::TYPE_NOTICE:
+ $text = $event->getArgument(1);
+ $clean = $this->cleanString($text);
+ if ($text != $clean) {
+ if ($this->config['censor.mode'] == 'censor') {
+ $event->setArgument(1, $clean);
+ } else {
+ $this->events->removeEvent($event);
+ }
+ }
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+<?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_Cron
+ * @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_Cron
+ */
+
+/**
+ * Allows callbacks to be registered for asynchronous execution.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_Cron
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_Cron
+ */
+class Phergie_Plugin_Cron extends Phergie_Plugin_Abstract
+{
+ /**
+ * Array of all registered callbacks with delays and arguments
+ *
+ * @var array
+ */
+ protected $callbacks;
+
+ /**
+ * Returns a human-readable representation of a callback for debugging
+ * purposes.
+ *
+ * @param callback $callback Callback to analyze
+ *
+ * @return string|boolean String representation of the callback or FALSE
+ * if the specified value is not a valid callback
+ */
+ protected function getCallbackString($callback)
+ {
+ if (!is_callable($callback)) {
+ return false;
+ }
+
+ if (is_array($callback)) {
+ $class = is_string($callback[0]) ?
+ $callback[0] : get_class($callback[0]);
+ $method = $class . '::' . $callback[1];
+ return $method;
+ }
+
+ return $callback;
+ }
+
+ /**
+ * Registers a callback for execution sometime after a given delay
+ * relative to now.
+ *
+ * @param callback $callback Callback to be registered
+ * @param int $delay Delay in seconds from now when the callback
+ * will be executed
+ * @param array $arguments Arguments to pass to the callback when
+ * it's executed
+ * @param bool $repeat TRUE to automatically re-register the
+ * callback for the same delay after it's executed, FALSE
+ * otherwise
+ *
+ * @return void
+ */
+ public function registerCallback($callback, $delay,
+ array $arguments = array(), $repeat = false)
+ {
+ $callbackString = $this->getCallbackString($callback);
+ if ($callbackString === false) {
+ echo 'DEBUG(Cron): Invalid callback specified - ',
+ var_export($callback, true), PHP_EOL;
+ return;
+ }
+
+ $registered = time();
+ $scheduled = $registered + $delay;
+
+ $this->callbacks[] = array(
+ 'callback' => $callback,
+ 'delay' => $delay,
+ 'arguments' => $arguments,
+ 'registered' => $registered,
+ 'scheduled' => $scheduled,
+ 'repeat' => $repeat,
+ );
+
+ echo 'DEBUG(Cron): Callback ', $callbackString,
+ ' scheduled for ', date('H:i:s', $scheduled), PHP_EOL;
+ }
+
+ /**
+ * Handles callback execution.
+ *
+ * @return void
+ */
+ public function onTick()
+ {
+ $now = time();
+ foreach ($this->callbacks as $key => &$callback) {
+ $callbackString = $this->getCallbackString($callback);
+
+ $scheduled = $callback['scheduled'];
+ if ($time < $scheduled) {
+ continue;
+ }
+
+ if (empty($callback['arguments'])) {
+ call_user_func($callback['callback']);
+ } else {
+ call_user_func_array(
+ $callback['callback'],
+ $callback['arguments']
+ );
+ }
+
+ echo 'DEBUG(Cron): Callback ', $callbackString,
+ ' scheduled for ', date('H:i:s', $scheduled), ',',
+ ' executed at ', date('H:i:s', $now), PHP_EOL;
+
+ if ($callback['repeat']) {
+ $callback['scheduled'] = $time + $callback['delay'];
+ echo 'DEBUG(Cron): Callback ', $callbackString,
+ ' scheduled for ', date('H:i:s', $callback['scheduled']),
+ PHP_EOL;
+ } else {
+ echo 'DEBUG(Cron): Callback ', $callbackString,
+ ' removed from callback list', PHP_EOL;
+ unset($this->callbacks[$key]);
+ }
+ }
+ }
+}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Exception related to plugin handling.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
class Phergie_Plugin_Exception extends Phergie_Exception
{
/**
- * Error indicating that a path containing plugins was specified, but
+ * Error indicating that a path containing plugins was specified, but
* did not reference a readable directory
*/
const ERR_DIRECTORY_NOT_READABLE = 1;
/**
- * Error indicating that an attempt was made to locate the class for a
+ * Error indicating that an attempt was made to locate the class for a
* specified plugin, but the class could not be found
*/
const ERR_CLASS_NOT_FOUND = 2;
/**
- * Error indicating that an attempt was made to locate the class for a
- * specified plugin, but that the found class did not extend the base
+ * Error indicating that an attempt was made to locate the class for a
+ * specified plugin, but that the found class did not extend the base
* plugin class
*/
const ERR_INCORRECT_BASE_CLASS = 3;
/**
- * Error indicating that an attempt was made to locate the class for a
+ * Error indicating that an attempt was made to locate the class for a
* specified plugin, but that the found class cannot be instantiated
*/
const ERR_CLASS_NOT_INSTANTIABLE = 4;
/**
- * Error indicating that an attempt was made to access a plugin that had
+ * Error indicating that an attempt was made to access a plugin that had
* not been loaded and autoloading was not enabled to load it
*/
const ERR_PLUGIN_NOT_LOADED = 5;
/**
- * Error indicating that an attempt was made to access the configuration
+ * Error indicating that an attempt was made to access the configuration
* handler before one had been set
*/
const ERR_NO_CONFIG_HANDLER = 6;
/**
- * Error indicating that an attempt was made to access the plugin
+ * Error indicating that an attempt was made to access the plugin
* handler before one had been set
*/
const ERR_NO_PLUGIN_HANDLER = 7;
/**
- * Error indicating that an attempt was made to access the event
+ * Error indicating that an attempt was made to access the event
* handler before one had been set
*/
const ERR_NO_EVENT_HANDLER = 8;
/**
- * Error indicating that an attempt was made to access the connection
+ * Error indicating that an attempt was made to access the connection
* before one had been set
*/
const ERR_NO_CONNECTION = 9;
/**
- * Error indicating that an attempt was made to access the current
+ * Error indicating that an attempt was made to access the current
* incoming event before one had been set
*/
const ERR_NO_EVENT = 10;
/**
- * Error indicating that a dependency of the plugin was unavailable at
+ * Error indicating that a dependency of the plugin was unavailable at
* the time that an attempt was made to load it
*/
const ERR_REQUIREMENT_UNSATISFIED = 11;
/**
- * Error indicating that a call was made to a nonexistent plugin method
- * and that its __call() implementation did not process that call as an
- * attempt to trigger an event - this is intended to aid in debugging of
+ * Error indicating that a call was made to a nonexistent plugin method
+ * and that its __call() implementation did not process that call as an
+ * attempt to trigger an event - this is intended to aid in debugging of
* such situations
*/
const ERR_INVALID_CALL = 12;
/**
- * Error indicating that a fatal runtime issue was encountered within a
+ * Error indicating that a fatal runtime issue was encountered within a
* plugin
*/
const ERR_FATAL_ERROR = 13;
* @pluginCmd [location] Get the location from Google Maps to the location specified
*/
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(
'sensor' => 'false',
'oe' => 'utf8',
'mrt' => 'all',
- 'key' => $this->_config['google.key']
+ 'key' => $this->getConfig('google.key')
);
$response = $this->http->get($url, $params);
- $json = (array) $response->getContent();
- $event = $this->getEvent();
- $source = $event->getSource();
- $nick = $event->getNick();
+ $json = $response->getContent();
if (!empty($json)) {
- $qtd = count($json['Placemark']);
+ $qtd = count($json->Placemark);
if ($qtd > 1) {
if ($qtd <= 3) {
- foreach ($json['Placemark'] as $places) {
- $xy = $places['Point']['coordinates'];
- $address = utf8_decode($places['address']);
+ 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);
}
} elseif ($qtd == 1) {
- $xy = $json['Placemark'][0]['Point']['coordinates'];
- $address = utf8_decode($json['Placemark'][0]['address']);
+ $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;
/**
* Performs a Google search to convert a value from one unit to another.
*
- * @param string $unit Source metric
- * @param string $to Value to be converted
- * @param string $unit2 Destination metric
+ * @param string $query Query of the form "[quantity] [unit] to [unit2]"
*
* @return void
*
- * @pluginCmd [unit] [to] [unit2] Convert a value from one metric to another
+ * @pluginCmd [quantity] [unit] to [unit2] Convert a value from one
+ * metric to another
*/
- public function onCommandConvert($unit, $to, $unit2)
+ public function onCommandConvert($query)
{
- $url = 'http://www.google.com/search?q='
- . urlencode($unit . ' ' . $to . ' ' . $unit2);
+ $url = 'http://www.google.com/search?q=' . urlencode($query);
$response = $this->http->get($url);
$contents = $response->getContent();
$event = $this->getEvent();
$source = $event->getSource();
$nick = $event->getNick();
- if (empty($contents)) {
- $this->doPrivmsg(
- $target,
- $nick . ', sorry, I can\'t give you an answer right now.'
- );
+ if ($response->isError()) {
+ $code = $response->getCode();
+ $message = $response->getMessage();
+ $this->doNotice($nick, 'ERROR: ' . $code . ' ' . $message);
return;
}
- $doc = new DomDocument;
- $doc->loadHTML($contents);
- foreach ($doc->getElementsByTagName('h2') as $element) {
- if ($element->getAttribute('class') == 'r') {
- $children = $element->childNodes;
- $text = str_replace(
- array(chr(195), chr(151), chr(160)),
- array('x', '', ' '),
- $children->item(0)->nodeValue
- );
- if ($children->length >= 3) {
- $text
- .= '^' . $children->item(1)->nodeValue
- . $children->item(2)->nodeValue;
- }
- }
+ $start = strpos($contents, '<h3 class=r>');
+ if ($start !== false) {
+ $end = strpos($contents, '</b>', $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->doPrivmsg($target, $nick . ', sorry I can\'t do that.');
+ $this->doNotice($nick, 'Sorry I couldn\'t find an answer.');
}
}
}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Handles on-demand loading of, iteration over, and access to plugins.
*
- * @category Phergie
+ * @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
+class Phergie_Plugin_Handler implements IteratorAggregate, Countable
{
/**
* Current list of plugin instances
protected $paths;
/**
- * Flag indicating whether plugin classes should be instantiated on
+ * Flag indicating whether plugin classes should be instantiated on
* demand if they are requested but no instance currently exists
*
* @var bool
protected $events;
/**
- * Constructor to initialize class properties and add the path for core
+ * Constructor to initialize class properties and add the path for core
* plugins.
*
* @param Phergie_Config $config configuration to pass to any
- * instantiated plugin
+ * instantiated plugin
* @param Phergie_Event_Handler $events event handler to pass to any
- * instantiated plugin
+ * instantiated plugin
*
* @return void
*/
* the reverse order in which they are added.
*
* @param string $path Filesystem directory path
- * @param string $prefix Optional class name prefix corresponding to the
+ * @param string $prefix Optional class name prefix corresponding to the
* path
*
* @return Phergie_Plugin_Handler Provides a fluent interface
}
/**
- * Adds a plugin instance to the handler.
+ * Returns metadata corresponding to a specified plugin.
*
- * @param string|Phergie_Plugin_Abstract $plugin Short name of the
+ * @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
+ * @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
}
// Attempt to locate and load the class
- foreach (array_reverse($this->paths) as $path) {
- $file = $path['path'] . $plugin . '.php';
- if (file_exists($file)) {
- include_once $file;
- $class = $path['prefix'] . $plugin;
- if (class_exists($class)) {
- break;
- }
- unset($class);
- }
- }
-
- // If the class can't be found, display an error
- if (!isset($class)) {
+ $info = $this->getPluginInfo($plugin);
+ $file = $info['file'];
+ $class = $info['class'];
+ include_once $file;
+ if (!class_exists($class, false)) {
throw new Phergie_Plugin_Exception(
- 'Class file for plugin "' . $plugin . '" cannot be found',
+ 'File "' . $file . '" does not contain class "' . $class . '"',
Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
);
}
- // Check to ensure the class is a plugin class
+ // Check to ensure the class is a plugin class
if (!is_subclass_of($class, 'Phergie_Plugin_Abstract')) {
$msg
- = 'Class for plugin "' . $plugin .
+ = 'Class for plugin "' . $plugin .
'" does not extend Phergie_Plugin_Abstract';
throw new Phergie_Plugin_Exception(
$msg,
$instance = new $class;
}
- // Configure and add the instance
- $instance->setPluginHandler($this);
- $instance->setConfig($this->config);
- $instance->setEventHandler($this->events);
- $instance->onLoad();
+ // Store the instance
$this->plugins[$index] = $instance;
$plugin = $instance;
$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,
+ * @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
/**
* Removes a plugin instance from the handler.
*
- * @param string|Phergie_Plugin_Abstract $plugin Short name of the
+ * @param string|Phergie_Plugin_Abstract $plugin Short name of the
* plugin class or a plugin object
*
* @return Phergie_Plugin_Handler Provides a fluent interface
}
/**
- * Returns the corresponding instance for a specified plugin, loading it
+ * 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
// If autoloading is disabled, display an error
if (!$this->autoload) {
- $msg
- = 'Plugin "' . $name . '" has been requested, ' .
+ $msg
+ = 'Plugin "' . $name . '" has been requested, ' .
'is not loaded, and autoload is disabled';
throw new Phergie_Plugin_Exception(
$msg,
}
/**
- * Returns the corresponding instances for multiple specified plugins,
- * loading them if they are not already loaded and autoloading is
+ * Returns the corresponding instances for multiple specified plugins,
+ * loading them if they are not already loaded and autoloading is
* enabled.
*
- * @param array $names List of short names of the plugin classes
+ * @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 plugin class short names to
- * corresponding plugin instances
+ * @return array Associative array mapping lowercased plugin class short
+ * names to corresponding plugin instances
*/
- public function getPlugins(array $names)
+ public function getPlugins(array $names = array())
{
+ if (empty($names)) {
+ return $this->plugins;
+ }
+
$plugins = array();
foreach ($names as $name) {
$plugins[$name] = $this->getPlugin($name);
}
/**
- * Returns whether or not at least one instance of a specified plugin
+ * Returns whether or not at least one instance of a specified plugin
* class is loaded.
*
* @param string $name Short name of the plugin class
}
/**
- * Sets a flag used to determine whether plugins should be loaded
+ * 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
+ * @param bool $flag TRUE to have plugins autoload (default), FALSE
* otherwise
*
* @return Phergie_Plugin_Handler Provides a fluent interface.
}
/**
- * Returns the value of a flag used to determine whether plugins should
+ * 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
}
/**
- * Allows plugin instances to be accessed as properties of the handler.
+ * Allows plugin instances to be accessed as properties of the handler.
*
* @param string $name Short name of the plugin
*
*/
public function __get($name)
{
- return $this->getPlugin($name);
+ return $this->getPlugin($name);
}
/**
}
/**
- * Proxies method calls to all plugins containing the called method. An
- * individual plugin may short-circuit this process by explicitly
+ * Proxies method calls to all plugins containing the called method. An
+ * individual plugin may short-circuit this process by explicitly
* returning FALSE.
*
* @param string $name Name of the method called
* @param array $args Arguments passed in the method call
*
- * @return bool FALSE if a plugin short-circuits processing by returning
+ * @return bool FALSE if a plugin short-circuits processing by returning
* FALSE, TRUE otherwise
*/
public function __call($name, array $args)
}
return true;
}
+
+ /**
+ * Returns the number of plugins contained within the handler.
+ *
+ * @return int Plugin count
+ */
+ public function count()
+ {
+ return count($this->plugins);
+ }
}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Help
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Provides access to descriptions of plugins and the commands they provide.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Help
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
}
/**
- * Displays a list of plugins with help information available or
+ * Displays a list of plugins with help information available or
* commands available for a specific plugin.
*
- * @param string $plugin Short name of the plugin for which commands
- * should be returned, else a list of plugins with help
+ * @param string $plugin Short name of the plugin for which commands
+ * should be returned, else a list of plugins with help
* information available is returned
*
* @return void
&& isset($this->registry[strtolower($plugin)]['cmd'])
) {
$msg
- = 'The ' .
- $plugin .
+ = 'The ' .
+ $plugin .
' plugin exposes the commands shown below.';
$this->doPrivMsg($nick, $msg);
if ($this->getConfig('command.prefix')) {
$msg
- = 'Note that these commands must be prefixed with "' .
+ = 'Note that these commands must be prefixed with "' .
$this->getConfig('command.prefix') .
'" (without quotes) when issued in a public channel.';
$this->doPrivMsg($nick, $msg);
/**
* Sets the description for the plugin instance
- *
+ *
* @param Phergie_Plugin_Abstract $plugin plugin instance
* @param string $description plugin description
- *
+ *
* @return void
*/
public function setPluginDescription(
/**
* Sets the description for the command on the plugin instance
- *
+ *
* @param Phergie_Plugin_Abstract $plugin plugin instance
* @param string $command from onCommand method
* @param string $description command description
* registers the plugin with the help plugin. this will parse the docblocks
* for specific annotations that this plugin will respond with when
* queried.
- *
+ *
* @param Phergie_Plugin_Abstract $plugin plugin instance
*
* @return void
}
/**
- * Taken from PHPUnit/Util/Test.php:436
+ * Taken from PHPUnit/Util/Test.php:243 and modified to fix an issue
+ * with tag content spanning multiple lines.
*
* PHPUnit
*
{
$annotations = array();
- $regex = '/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m';
+ $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) {
- $annotations[$matches['name'][$i]][] = $matches['value'][$i];
+ $annotation = $matches['value'][$i];
+ $annotation = preg_replace('/\s*\v+\s*\*\s*/', ' ', $annotation);
+ $annotation = rtrim($annotation);
+ $annotations[$matches['name'][$i]][] = $annotation;
}
}
*/
protected $code;
+ /**
+ * HTTP response strings
+ *
+ * @var array
+ */
+ protected $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
return $this->code;
}
+ /**
+ * Returns the HTTP response code text.
+ *
+ * @return string Response code text
+ */
+ public function getCodeAsString()
+ {
+ $code = $this->code;
+
+ if (!isset($this->codeStrings[$code])) {
+ return 'Unkown HTTP Status';
+ }
+
+ return $this->codeStrings[$code];
+ }
+
/**
* Returns whether the response indicates a client- or server-side error.
*
*/
public function onConnect()
{
- // Construct a new data source
$this->source = new Phergie_Plugin_Php_Source_Local;
}
*/
public function onCommandPhp($functionName)
{
- // Search for the function
+ $nick = $this->event->getNick();
if ($function = $this->source->findFunction($functionName)) {
- $msg = 'PHP ' . $function['name'] . ': ' . $function['description'];
+ $msg = $nick . ': ' . $function['description'];
+ $this->doPrivmsg($this->event->getSource(), $msg);
} else {
$msg = 'Search for function ' . $functionName . ' returned no results.';
+ $this->doNotice($nick, $msg);
}
-
- // Return the result to the source
- $this->doPrivmsg($this->getEvent()->getSource(), $msg);
}
}
// @todo Modify this to be rethrown as an appropriate
// Phergie_Plugin_Exception and handled in Phergie_Plugin_Php
} catch (PDOException $e) {
- }
+ echo 'PDO failure: '.$e->getMessage();
+ }
}
/**
protected function buildDatabase($rebuild = false)
{
// Check to see if the functions table exists
- $table = $this->database->exec("SELECT COUNT(*) FROM `sqlite_master` WHERE `name` = 'functions'");
-
+ $checkstmt = $this->database->query("SELECT COUNT(*) FROM `sqlite_master` WHERE `name` = 'functions'");
+ $checkstmt->execute();
+ $result = $checkstmt->fetch(PDO::FETCH_ASSOC);
+ unset( $checkstmt );
+ $table = $result['COUNT(*)'];
+ unset( $result );
// If the table doesn't exist, create it
if (!$table) {
- $this->database->exec('CREATE TABLE `functions` (`name` VARCHAR(255), `description` TEXT)');
- $this->database->exec('CREATE UNIQUE INDEX `functions_name` ON `functions` (`name`)');
+ $this->database->exec('CREATE TABLE `functions` (`name` VARCHAR(255), `description` TEXT)');
+ $this->database->exec('CREATE UNIQUE INDEX `functions_name` ON `functions` (`name`)');
}
// If we created a new table, fill it with data
// ... it's the last part of the complete function description
$completeLine = $firstPart . ' ' . $line;
$firstPart = '';
- if (preg_match('{^([^\s]*)[\s]?([^)]*)\(([^\)]*)\)[\sU]+([\sa-zA-Z0-9\.\-_]*)$}', $completeLine, $matches)) {
+ if (preg_match('{^([^\s]*)[\s]?([^)]*)\(([^\)]*)\)[\sU]+([\sa-zA-Z0-9\.,\-_()]*)$}', $completeLine, $matches)) {
$valid[] = $matches;
}
}
--- /dev/null
+<?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_Reload
+ * @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_Reload
+ */
+
+/**
+ * Facilitates reloading of individual plugins for development purposes.
+ * Note that, because existing class definitions cannot be removed from
+ * memory, increased memory usage is an expected result of using this plugin.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_Reload
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_Reload
+ * @uses Phergie_Plugin_Command pear.phergie.org
+ */
+class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract
+{
+ /**
+ * Checks for dependencies.
+ *
+ * @return void
+ */
+ public function onLoad()
+ {
+ $this->getPluginHandler()->getPlugin('Command');
+ }
+
+ /**
+ * Reloads a specified plugin.
+ *
+ * @param string $plugin Short name of the plugin to reload
+ *
+ * @return void
+ */
+ public function onCommandReload($plugin)
+ {
+ $plugin = ucfirst($plugin);
+
+ if (!$this->plugins->hasPlugin($plugin)) {
+ echo 'DEBUG(Reload): ' . ucfirst($plugin) . ' is not loaded yet, loading', PHP_EOL;
+ $this->plugins->getPlugin($plugin);
+ return;
+ }
+
+ try {
+ $info = $this->plugins->getPluginInfo($plugin);
+ } catch (Phergie_Plugin_Exception $e) {
+ $source = $this->event->getSource();
+ $nick = $this->event->getNick();
+ $this->doNotice($source, $nick . ': ' . $e->getMessage());
+ return;
+ }
+
+ $class = $info['class'];
+ $contents = file_get_contents($info['file']);
+ $newClass = $class . '_' . sha1($contents);
+
+ if (class_exists($newClass, false)) {
+ echo 'DEBUG(Reload): Class ', $class, ' has not changed since last reload', PHP_EOL;
+ return;
+ }
+
+ $contents = preg_replace(
+ array('/<\?(?:php)?/', '/class\s+' . $class . '/i'),
+ array('', 'class ' . $newClass),
+ $contents
+ );
+ eval($contents);
+
+ $instance = new $newClass;
+ $instance->setName($plugin);
+ $this->plugins
+ ->removePlugin($plugin)
+ ->addPlugin($instance);
+
+ echo 'DEBUG(Reload): Reloaded ', $class, ' to ', $newClass, PHP_EOL;
+ }
+}
* @package Phergie_Plugin_Remind
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
- * @link http://pear.phergie.org/package/Phergie_Plugin_Drink
+ * @link http://pear.phergie.org/package/Phergie_Plugin_Remind
* @uses Phergie_Plugin_Command pear.phergie.org
- * @uses Phergie_Plugin_Helper_Time pear.phergie.org
+ * @uses Phergie_Plugin_Time pear.phergie.org
*/
class Phergie_Plugin_Remind extends Phergie_Plugin_Abstract
{
/**
* Number of reminders to show in public.
*/
- const PUBLIC_REMINDERS = 3;
+ protected $publicReminders = 3;
/**
* PDO resource for a SQLite database containing the reminders.
*/
public function onLoad()
{
- $this->getPluginHandler()->getPlugin('Command');
- $path = dirname(__FILE__) . '/reminder.db';
+ $plugins = $this->getPluginHandler();
+ $plugins->getPlugin('Command');
+ $plugins->getPlugin('Time');
+ }
+
+ /**
+ * Creates the database if it does not already exist.
+ *
+ * @return void
+ */
+ public function onConnect()
+ {
+ $dir = dirname(__FILE__) . '/' . $this->getName();
+ $path = $dir . '/reminder.db';
+ if (!file_exists($dir)) {
+ mkdir($dir);
+ }
if (isset($this->config['remind.use_memory'])) {
- $this->keepListInMemory = (bool)$this->config['remind.use_memory'];
+ $this->keepListInMemory = (bool) $this->config['remind.use_memory'];
+ }
+
+ if (isset($this->config['remind.public_reminders'])) {
+ $this->publicReminders = (int) $this->config['remind.public_reminders'];
+ $this->publicReminders = max($this->publicReminders, 0);
}
try {
// fetch and deliver messages
$reminders = $this->fetchMessages($channel, $nick);
- if (count($reminders) > self::PUBLIC_REMINDERS) {
- $msgs = array_slice($reminders, 0, self::PUBLIC_REMINDERS);
- $privmsgs = array_slice($reminders, self::PUBLIC_REMINDERS);
+ if (count($reminders) > $this->publicReminders) {
+ $msgs = array_slice($reminders, 0, $this->publicReminders);
+ $privmsgs = array_slice($reminders, $this->publicReminders);
} else {
$msgs = $reminders;
$privmsgs = false;
}
foreach ($msgs as $msg) {
- $ts = new Phergie_Plugin_Helper_Time($msg['time']);
+ $ts = $this->plugins->time->getCountdown($msg['time']);
$formatted = sprintf(
'%s: (from %s, %s ago) %s',
- $nick, $msg['sender'], $ts->getCountdown(), $msg['message']
+ $nick, $msg['sender'], $ts, $msg['message']
);
$this->doPrivmsg($channel, $formatted);
$this->deleteMessage($msg['rowid'], $channel, $nick);
if ($privmsgs) {
foreach ($privmsgs as $msg) {
- $ts = new Phergie_Plugin_Helper_Time($msg['time']);
+ $ts = $this->plugins->time->getCountdown($msg['time']);
$formatted = sprintf(
'from %s, %s ago: %s',
- $msg['sender'], $ts->getCountdown(), $msg['message']
+ $msg['sender'], $ts, $msg['message']
);
$this->doPrivmsg($nick, $formatted);
$this->deleteMessage($msg['rowid'], $channel, $nick);
--- /dev/null
+<?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_TerryChay
+ * @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_TerryChay
+ */
+
+/**
+ * Handles requests for checking spelling of specified words and returning
+ * either confirmation of correctly spelled words or potential correct
+ * spellings for misspelled words.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_SpellCheck
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_TerryChay
+ * @uses Phergie_Plugin_Command pear.phergie.org
+ * @uses extension pspell
+ */
+class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract
+{
+
+ /**
+ * Spell check dictionary handler
+ *
+ * @var resource
+ */
+ protected $pspell;
+
+ /**
+ * Limit on the number of potential correct spellings returned
+ *
+ * @var int
+ */
+ protected $limit;
+
+ /**
+ * Check for dependencies.
+ *
+ * @return void
+ */
+ public function onLoad()
+ {
+ if (!extension_loaded('pspell')) {
+ $this->fail('pspell php extension is required');
+ }
+
+ if (!$this->getConfig('spellcheck.lang')) {
+ $this->fail('Setting spellcheck.lang must be filled-in');
+ }
+
+ $this->plugins->getPlugin('Command');
+
+ set_error_handler(array($this, 'loadDictionaryError'));
+ $this->pspell = pspell_new($this->getConfig('spellcheck.lang'));
+ restore_error_handler();
+
+ $this->limit = $this->getConfig('spellcheck.limit', 5);
+ }
+
+ /**
+ * Intercepts and handles requests for spell checks.
+ *
+ * @param string $word the string to perform checks against
+ *
+ * @return void
+ */
+ public function onCommandSpell($word)
+ {
+ $source = $this->event->getSource();
+ $target = $this->event->getNick();
+
+ $message = $target . ': The word "' . $word;
+ $message .= '" seems to be spelt correctly.';
+ if (!pspell_check($this->pspell, $word)) {
+ $suggestions = pspell_suggest($this->pspell, $word);
+
+ $message = $target;
+ $message .= ': I could not find any suggestions for "' . $word . '".';
+ if (!empty($suggestions)) {
+ $suggestions = array_splice($suggestions, 0, $this->limit);
+ $message = $target . ': Suggestions for "';
+ $message .= $word . '": ' . implode(', ', $suggestions) . '.';
+ }
+ }
+
+ $this->doPrivmsg($source, $message);
+ }
+
+ /**
+ * Handle any errors from loading dictionary
+ *
+ * @param integer $errno Error code
+ * @param string $errstr Error message
+ * @param string $errfile File that errored
+ * @param integer $errline Line where the error happened
+ *
+ * @return void
+ */
+ protected function loadDictionaryError($errno, $errstr, $errfile, $errline)
+ {
+ $this->fail($errstr);
+ }
+
+}
--- /dev/null
+<?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_Url
+ * @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_Url
+ */
+
+/**
+ * Responds to a request for a TLD (formatted as .tld where tld is the TLD to
+ * be looked up) with its corresponding description.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_Tld
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_Tld
+ * @uses Phergie_Plugin_Http pear.phergie.org
+ *
+ * @pluginDesc Provides information for a top level domain.
+ */
+class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract
+{
+ /**
+ * connection to the database
+ * @var PDO
+ */
+ protected $db;
+ /**
+ * Some fixed TLD values, keys must be lowercase
+ * @var array
+ */
+ protected static $fixedTlds;
+
+ /**
+ * Prepared statement for selecting a single tld
+ * @var PDOStatement
+ */
+ protected $select;
+
+ /**
+ * Prepared statement for selecting all tlds
+ * @var PDOStatement
+ */
+ protected $selectAll;
+
+ /**
+ * Checks for dependencies, sets up database and hard coded values
+ *
+ * @return void
+ */
+ public function onLoad()
+ {
+ $help = $this->getPluginHandler()->getPlugin('Help');
+ $help->register($this);
+
+ if (!is_array(self::$fixedTlds)) {
+ self::$fixedTlds = array(
+ 'phergie' => 'You can find Phergie at http://www.phergie.org',
+ 'spoon' => 'Don\'t you know? There is no spoon!',
+ 'poo' => 'Do you really think that\'s funny?',
+ 'root' => 'Diagnostic marker to indicate '
+ . 'a root zone load was not truncated.'
+ );
+ }
+
+ try {
+ $dbFile = dirname(__FILE__) . '/Tld/tld.db';
+ $dbManager = new Phergie_Db_Sqlite($dbFile);
+ $this->db = $dbManager->getDb();
+ if (!$dbManager->hasTable('tld')) {
+ $query = 'CREATE TABLE tld ('
+ . 'tld VARCHAR(20), '
+ . 'type VARCHAR(20), '
+ . 'description VARCHAR(255))';
+
+ $this->db->exec($query);
+
+ // prepare a statement to populate the table with
+ // tld information
+ $insert = $this->db->prepare(
+ 'INSERT INTO tld
+ (tld, type, description)
+ VALUES (:tld, :type, :description)'
+ );
+
+ // grab tld data from iana.org...
+ $contents = file_get_contents(
+ 'http://www.iana.org/domains/root/db/'
+ );
+
+ // ...and then parse it out
+ $regex = '{<tr class="iana-group[^>]*><td><a[^>]*>\s*\.?([^<]+)\s*'
+ . '(?:<br/><span[^>]*>[^<]*</span>)?</a></td><td>\s*'
+ . '([^<]+)\s*</td><td>\s*([^<]+)\s*}i';
+ preg_match_all($regex, $contents, $matches, PREG_SET_ORDER);
+
+ foreach ($matches as $match) {
+ list(, $tld, $type, $description) = array_pad($match, 4, null);
+ $type = trim(strtolower($type));
+ if ($type != 'test') {
+ $tld = trim(strtolower($tld));
+ $description = trim($description);
+
+ switch ($tld) {
+
+ case 'com':
+ $description = 'Commercial';
+ break;
+
+ case 'info':
+ $description = 'Information';
+ break;
+
+ case 'net':
+ $description = 'Network';
+ break;
+
+ case 'org':
+ $description = 'Organization';
+ break;
+
+ case 'edu':
+ $description = 'Educational';
+ break;
+
+ case 'name':
+ $description = 'Individuals, by name';
+ break;
+ }
+
+ if (empty($tld) || empty($description)) {
+ continue;
+ }
+
+ $regex = '{(^(?:Reserved|Restricted)\s*(?:exclusively\s*)?'
+ . '(?:for|to)\s*(?:members of\s*)?(?:the|support)?'
+ . '\s*|\s*as advised.*$)}i';
+ $description = preg_replace($regex, '', $description);
+ $description = ucfirst(trim($description));
+
+ $data = array_map(
+ 'html_entity_decode', array(
+ 'tld' => $tld,
+ 'type' => $type,
+ 'description' => $description
+ )
+ );
+
+ $insert->execute($data);
+ }
+ }
+
+ unset(
+ $insert,
+ $matches,
+ $match,
+ $contents,
+ $tld,
+ $type,
+ $description,
+ $data,
+ $regex
+ );
+ }
+
+ // Create a prepared statements for retrieving TLDs
+ $this->select = $this->db->prepare(
+ 'SELECT type, description '
+ . 'FROM tld WHERE LOWER(tld) = LOWER(:tld)'
+ );
+
+ $this->selectAll = $this->db->prepare(
+ 'SELECT tld, type, description FROM tld'
+ );
+ } catch (PDOException $e) {
+ }
+ }
+
+ /**
+ * takes a tld in the format '.tld' and returns its related data
+ *
+ * @param string $tld tld to process
+ *
+ * @return null
+ *
+ * @pluginCmd .[tld] request details about the tld
+ */
+ public function onCommandTld($tld)
+ {
+ $tld = ltrim($tld, '.');
+ $description = $this->getTld($tld);
+ $this->doPrivmsg(
+ $this->event->getSource(),
+ "{$this->getEvent()->getNick()}: .{$tld} -> "
+ . ($description ? $description : 'Unknown TLD')
+ );
+ }
+
+ /**
+ * Retrieves the definition for a given TLD if it exists
+ *
+ * @param string $tld TLD to search for
+ *
+ * @return string Definition of the given TLD
+ */
+ public function getTld($tld)
+ {
+ $tld = trim(strtolower($tld));
+ if (isset(self::$fixedTlds[$tld])) {
+ return self::$fixedTlds[$tld];
+ } else {
+ if ($this->select->execute(array('tld' => $tld))) {
+ $tlds = $this->select->fetch();
+ if (is_array($tlds)) {
+ return '(' . $tlds['type'] . ') ' . $tlds['description'];
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves a list of all the TLDs and their definitions
+ *
+ * @return array Array of all the TLDs and their definitions
+ */
+ public function getTlds()
+ {
+ if ($this->selectAll->execute()) {
+ $tlds = $this->selectAll->fetchAll();
+ if (is_array($tlds)) {
+ $tldinfo = array();
+ foreach ($tlds as $key => $tld) {
+ if (!empty($tld['tld'])) {
+ $tldinfo[$tld['tld']] = "({$tld['type']}) "
+ . $tld['description'];
+ }
+ }
+ unset($tlds);
+ return $tldinfo;
+ }
+ }
+ return false;
+ }
+}
+
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Url
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
* Monitors incoming messages for instances of URLs and responds with messages
* containing relevant information about detected URLs.
*
- * Has an utility method accessible via
+ * Has an utility method accessible via
* $this->getPlugin('Url')->getTitle('http://foo..').
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Url
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Url
+ * @uses Phergie_Plugin_Http pear.phergie.org
*/
class Phergie_Plugin_Url extends Phergie_Plugin_Abstract
{
/**
* Links output format
*
- * Can use the variables %nick%, %title% and %link% in it to display
+ * Can use the variables %nick%, %title% and %link% in it to display
* page titles and links
*
* @var string
*/
- protected $baseFormat = '%nick%: %message%';
+ protected $baseFormat = '%message%';
protected $messageFormat = '[ %link% ] %title%';
/**
- * Flag indicating whether a single response should be sent for a single
+ * Flag indicating whether a single response should be sent for a single
* message containing multiple links
*
* @var bool
protected $titleLength = 40;
/**
- * Url cache to prevent spamming, especially with multiple bots on the
+ * Url cache to prevent spamming, especially with multiple bots on the
* same channel
*
* @var array
protected $limit = 10;
/**
- * Flag that determines if the plugin will fall back to using an HTTP
- * stream when a URL using SSL is detected and OpenSSL support isn't
- * available in the PHP installation in use
+ * Flag that determines if the plugin will fall back to using an HTTP
+ * stream when a URL using SSL is detected and OpenSSL support isn't
+ * available in the PHP installation in use
*
* @var bool
*/
protected $sslFallback = true;
/**
- * Flag that is set to true by the custom error handler if an HTTP error
+ * Flag that is set to true by the custom error handler if an HTTP error
* code has been received
*
* @var boolean
protected $errorMessage = null;
/**
- * Flag indicating whether or not to display error messages as the title
+ * Flag indicating whether or not to display error messages as the title
* if a link posted encounters an error
*
* @var boolean
protected $detectSchemeless = false;
/**
- * List of error messages to return when the requested URL returns an
+ * List of error messages to return when the requested URL returns an
* HTTP error
*
* @var array
// make the shortener configurable
$shortener = $this->getConfig('url.shortener', 'Trim');
$shortener = "Phergie_Plugin_Url_Shorten_{$shortener}";
- $this->shortener = new $shortener;
+ $this->shortener = new $shortener($this->plugins->getPlugin('Http'));
if (!$this->shortener instanceof Phergie_Plugin_Url_Shorten_Abstract) {
$this->fail("Declared shortener class {$shortener} is not of proper ancestry");
// Get a list of valid TLDs
if (!is_array($this->tldList) || count($this->tldList) <= 6) {
- /* Omitted for port
- if ($this->pluginLoaded('Tld')) {
- $this->tldList = Phergie_Plugin_Tld::getTlds();
- if (is_array($this->tldList)) {
- $this->tldList = array_keys($this->tldList);
- }
- }
- */
- if (!is_array($this->tldList) || count($this->tldList) <= 0) {
- $this->tldList = array('ac', 'ad', 'ae', 'aero', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'arpa', 'as', 'asia', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi', 'biz', 'bj', 'bl', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cat', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'com', 'coop', 'cr', 'cu', 'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'edu', 'ee', 'eg', 'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gl', 'gm', 'gn', 'gov', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'info', 'int', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm', 'jo', 'jobs', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly', 'ma', 'mc', 'md', 'me', 'mf', 'mg', 'mh', 'mil', 'mk', 'ml', 'mm', 'mn', 'mo', 'mobi', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'name', 'nc', 'ne', 'net', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'org', 'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'pro', 'ps', 'pt', 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tel', 'tf', 'tg', 'th', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'travel', 'tt', 'tv', 'tw', 'tz', 'ua', 'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'yu', 'za', 'zm', 'zw');
- }
+ $tldPath = dirname(__FILE__) . '/Url/url.tld.txt';
+ $this->tldList = explode("\n", file_get_contents($tldPath));
+ $this->debug('Loaded ' . count($this->tldList) . ' tlds');
rsort($this->tldList);
}
'merge_links' => 'mergeLinks',
'title_length' => 'titleLength',
'show_errors' => 'showErrors',
+ 'expire' => 'expire',
) as $config => $local) {
if (isset($this->config["url.{$config}"])) {
$this->$local = $this->config["uri.{$config}"];
* found, responds with its title if it is an HTML document and the
* shortened equivalent of its original URL if it meets length requirements.
*
- * @todo Update this to pull configuration settings from $this->config
+ * @todo Update this to pull configuration settings from $this->config
* rather than caching them as class properties
* @return void
*/
// Convert url
$shortenedUrl = $this->shortener->shorten($url);
+ if (!$shortenedUrl) {
+ $this->debug('Invalid Url: Unable to shorten. (' . $url . ')');
+ continue;
+ }
// Prevent spamfest
if ($this->checkUrlCache($url, $shortenedUrl)) {
*/
public function getTitle($url)
{
- $opts = array(
- 'http' => array(
- 'timeout' => 3.5,
- 'method' => 'GET',
- 'user_agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12'
- )
+ $http = $this->plugins->getPlugin('Http');
+ $options = array(
+ 'timeout' => 3.5,
+ 'user_agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12'
);
- $context = stream_context_create($opts);
-
- if ($page = fopen($url, 'r', false, $context)) {
- stream_set_timeout($page, 3.5);
- $data = stream_get_meta_data($page);
- foreach ($data['wrapper_data'] as $header) {
- if (preg_match('/^Content-Type: ([^;]+)/', $header, $match)
- && !preg_match('#^(text/x?html|application/xhtml+xml)$#', $match[1])
- ) {
- $title = $match[1];
- }
- }
- if (!isset($title)) {
- $content = '';
- $tstamp = time() + 5;
-
- while ($chunk = fread($page, 64)) {
- $data = stream_get_meta_data($page);
- if ($data['timed_out']) {
- $this->debug('Url Timed Out: ' . $url);
- $this->errorStatus = true;
- break;
- }
- $content .= $chunk;
- // Check for timeout
- if (time() > $tstamp) break;
- // Try to read title
- if (preg_match('#<title[^>]*>(.*)#is', $content, $m)) {
- // Start another loop to grab some more data in order to be sure we have the complete title
- $content = $m[1];
- $loop = 2;
- while (($chunk = fread($page, 64)) && $loop-- && !strstr($content, '<')) {
- $content .= $chunk;
- // Check for timeout
- if (time() > $tstamp) break;
- }
- preg_match('#^([^<]*)#is', $content, $m);
- $title = preg_replace('#\s+#', ' ', $m[1]);
- $title = trim($this->decode($title, $this->titleLength));
- break;
- }
- // Title won't appear beyond that point so stop parsing
- if (preg_match('#</head>|<body#i', $content)) {
- break;
- }
- }
+
+ $response = $http->get($url, array(), $options);
+
+ $header = $response->getHeaders('Content-Type');
+ if (!preg_match('#^(text/x?html|application/xhtml+xml)(?:;.*)?$#', $header)) {
+ $title = $header;
+ }
+
+ $content = $response->getContent();
+ if (empty($title)) {
+ if (preg_match('#<title[^>]*>(.*?)</title>#is', $content, $match)) {
+ $title = html_entity_decode(trim($match[1]));
}
- fclose($page);
- } else if (!$this->errorStatus) {
- $this->debug('Couldn\t Open Url: ' . $url);
}
if (empty($title)) {
- if ($this->errorStatus) {
- if (!$this->showErrors || empty($this->errorMessage)) {
- return;
- }
- $title = $this->errorMessage;
- $this->errorStatus = false;
- $this->errorMessage = null;
+ if ($response->isError()) {
+ $title = $response->getCodeAsString();
} else {
$title = 'No Title';
}
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Url
+ * @uses Phergie_Plugin_Http pear.phergie.org
*/
abstract class Phergie_Plugin_Url_Shorten_Abstract
{
+ protected $http;
+
+ /**
+ * Constructor
+ *
+ * @param Phergie_Plugin_Http $http instance of the http plugin
+ */
+ public function __construct(Phergie_Plugin_Http $http)
+ {
+ $this->http = $http;
+ }
+
/**
- * Takes a long url and returns a shortened link
+ * Returns an array of request parameters given a url to shorten. The
+ * following keys are valid request parameters:
+ *
+ * * 'uri': the URI for the request (required)
+ * * 'query': an array of key-value pairs sent in a GET request
+ * * 'post': an array of key-value pairs sent in a POST request
+ * * 'callback': to be called after the request is finished. Should accept
+ * a Phergie_Plugin_Http_Response object and return either the shortened
+ * url or false if an error has occured.
+ *
+ * If the 'post' key is present a POST request shall be made; otherwise
+ * a GET request will be made. The 'post' key can be an empty array and
+ * a post request will still be made.
+ *
+ * If no callback is provided the contents of the response will be returned.
*
* @param string $url the url to shorten
*
- * @return string string the shortened url
+ * @return array the request parameters
*/
- public abstract function shorten($url);
+ protected abstract function getRequestParams($url);
+
+ /**
+ * Shortens a given url.
+ *
+ * @param string $url the url to shorten
+ *
+ * @return string the shortened url or false on a failure
+ */
+ public function shorten($url)
+ {
+ $defaults = array('get' => array(), 'post' => array(), 'callback' => null);
+ $options = array('timeout' => 2);
+ $params = $this->getRequestParams($url) + $defaults;
+
+ // Should some kind of notice be thrown? Maybe just if getRequestParams does not return an array?
+ if (!is_array($params) || empty($params['uri'])) {
+ return $url;
+ }
+
+ if (!empty($params['post'])) {
+ $response = $this->http->post($params['uri'], $params['get'], $params['post'], $options);
+ } else {
+ $response = $this->http->get($params['uri'], $params['get'], $options);
+ }
+
+ if (is_callable($params['callback'])) {
+ return call_user_func($params['callback'], $response);
+ }
+
+ $code = $response->getCode();
+ $content = trim($response->getContent);
+ if ($code < 200 || $code >= 300 || empty($content)) {
+ return false;
+ }
+
+ return $response->getContent();
+ }
}
class Phergie_Plugin_Url_Shorten_Trim extends Phergie_Plugin_Url_Shorten_Abstract
{
/**
- * Short a URL through the tr.im api
+ * Returns an array of request parameters given a url to shorten. The
+ * following keys are valid request parameters:
*
* @param string $url the url to shorten
*
- * @return string string the shortened url
+ * @return array the request parameters
*/
- public function shorten($url)
+ protected function getRequestParams($url)
{
- return file_get_contents('http://api.tr.im/v1/trim_simple?url=' . rawurlencode($url));
+ return array(
+ 'uri' => 'http://api.tr.im/v1/trim_simple?url=' . rawurlencode($url),
+ 'callback' => array($this, 'onComplete')
+ );
+ }
+
+ /**
+ * Callback for when the URL has been shortened. Checks for error messages.
+ *
+ * @param Phergie_Plugin_Http_Response $response the response object
+ *
+ * @return string|bool the shortened url or false on failure
+ */
+ protected function onComplete($response)
+ {
+ if (strpos($response->getContent(), 'Error: ') === 0) {
+ return false;
+ }
+
+ return $response->getContent();
}
}
--- /dev/null
+ac
+ad
+ae
+aero
+af
+ag
+ai
+al
+am
+an
+ao
+aq
+ar
+arpa
+as
+asia
+at
+au
+aw
+ax
+az
+ba
+bb
+bd
+be
+bf
+bg
+bh
+bi
+biz
+bj
+bl
+bm
+bn
+bo
+br
+bs
+bt
+bv
+bw
+by
+bz
+ca
+cat
+cc
+cd
+cf
+cg
+ch
+ci
+ck
+cl
+cm
+cn
+co
+com
+coop
+cr
+cu
+cv
+cx
+cy
+cz
+de
+dj
+dk
+dm
+do
+dz
+ec
+edu
+ee
+eg
+eh
+er
+es
+et
+eu
+fi
+fj
+fk
+fm
+fo
+fr
+ga
+gb
+gd
+ge
+gf
+gg
+gh
+gi
+gl
+gm
+gn
+gov
+gp
+gq
+gr
+gs
+gt
+gu
+gw
+gy
+hk
+hm
+hn
+hr
+ht
+hu
+id
+ie
+il
+im
+in
+info
+int
+io
+iq
+ir
+is
+it
+je
+jm
+jo
+jobs
+jp
+ke
+kg
+kh
+ki
+km
+kn
+kp
+kr
+kw
+ky
+kz
+la
+lb
+lc
+li
+lk
+lr
+ls
+lt
+lu
+lv
+ly
+ma
+mc
+md
+me
+mf
+mg
+mh
+mil
+mk
+ml
+mm
+mn
+mo
+mobi
+mp
+mq
+mr
+ms
+mt
+mu
+museum
+mv
+mw
+mx
+my
+mz
+na
+name
+nc
+ne
+net
+nf
+ng
+ni
+nl
+no
+np
+nr
+nu
+nz
+om
+org
+pa
+pe
+pf
+pg
+ph
+pk
+pl
+pm
+pn
+pr
+pro
+ps
+pt
+pw
+py
+qa
+re
+ro
+rs
+ru
+rw
+sa
+sb
+sc
+sd
+se
+sg
+sh
+si
+sj
+sk
+sl
+sm
+sn
+so
+sr
+st
+su
+sv
+sy
+sz
+tc
+td
+tel
+tf
+tg
+th
+tj
+tk
+tl
+tm
+tn
+to
+tp
+tr
+travel
+tt
+tv
+tw
+tz
+ua
+ug
+uk
+um
+us
+uy
+uz
+va
+vc
+ve
+vg
+vi
+vn
+vu
+wf
+ws
+ye
+yt
+yu
+za
+zm
+zw
--- /dev/null
+<?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_Weather
+ * @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_Weather
+ */
+
+/**
+ * Detects and responds to requests for current weather conditions in a
+ * particular location using data from a web service. Requires registering
+ * with weather.com to obtain authentication credentials, which must be
+ * stored in the configuration settings weather.partner_id and
+ * weather.license_key for the plugin to function.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_Weather
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_Weather
+ * @link http://www.weather.com/services/xmloap.html
+ * @uses Phergie_Plugin_Command pear.phergie.org
+ * @uses Phergie_Plugin_Http pear.phergie.org
+ * @uses extension SimpleXML
+ */
+class Phergie_Plugin_Weather extends Phergie_Plugin_Abstract
+{
+ /**
+ * Checks for dependencies.
+ *
+ * @return void
+ */
+ public function onLoad()
+ {
+ $plugins = $this->getPluginHandler();
+ $plugins->getPlugin('Command');
+ $plugins->getPlugin('Http');
+
+ if (empty($this->config['weather.partner_id'])
+ || empty($this->config['weather.license_key'])) {
+ $this->fail('weather.partner_id and weather.license_key must be specified');
+ }
+ }
+
+ /**
+ * Returns a weather report for a specified location.
+ *
+ * @param string $location Zip code or city/state/country specification
+ *
+ * @return void
+ */
+ public function onCommandWeather($location)
+ {
+ $response = $this->plugins->http->get(
+ 'http://xoap.weather.com/search/search',
+ array('where' => $location)
+ );
+
+ if ($response->isError()) {
+ $this->doNotice(
+ $this->event->getNick(),
+ 'ERROR: ' . $response->getCode() . ' ' . $response->getMessage()
+ );
+ return;
+ }
+
+ $nick = $this->event->getNick();
+
+ $xml = $response->getContent();
+ if (count($xml->loc) == 0) {
+ $this->doNotice($nick, 'No results for that location.');
+ return;
+ }
+
+ $where = (string) $xml->loc[0]['id'];
+ $response = $this->plugins->http->get(
+ 'http://xoap.weather.com/weather/local/' . $where,
+ array(
+ 'cc' => '*',
+ 'link' => 'xoap',
+ 'prod' => 'xoap',
+ 'par' => $this->config['weather.partner_id'],
+ 'key' => $this->config['weather.license_key'],
+ )
+ );
+
+ if ($response->isError()) {
+ $this->doNotice(
+ $this->event->getNick(),
+ 'ERROR: ' . $response->getCode() . ' ' . $response->getMessage()
+ );
+ return;
+ }
+
+ $xml = $response->getContent();
+ $weather = 'Weather for ' . (string) $xml->loc->dnam . ' - ';
+ $weather .= 'Current temperature ' .
+ (string) $xml->cc->tmp .
+ (string) $xml->head->ut . ' / ';
+ if ((string) $xml->head->ut == 'F') {
+ $weather .= round(((((int) $xml->cc->tmp - 32) * 5) / 9)) . 'C';
+ } else {
+ $weather .= round(((((int) $xml->cc->tmp * 9) / 5) + 32)) . 'F';
+ }
+ $weather .=
+ ', Relative humidity ' . (string) $xml->cc->hmid . '%' .
+ ', Current conditions ' . (string) $xml->cc->t .
+ ', Last update ' . (string) $xml->cc->lsup .
+ ' [ http://weather.com/weather/today/' .
+ str_replace(
+ array('(', ')', ',', ' '),
+ array('', '', '', '+'),
+ (string) $xml->loc->dnam
+ ) .
+ ' ]';
+
+ $this->doPrivmsg($this->event->getSource(), $nick . ': ' . $weather);
+ }
+}
'realname' => 'Matthew Turland',
'nick' => 'Phergie2',
// 'password' => 'password goes here if needed',
- // 'transport' => 'ssl' // uncomment to connect using SSL
+ // 'transport' => 'ssl', // uncomment to connect using SSL
+ // 'encoding' => 'UTF8', // uncomment if using UTF8
)
),
-
- 'processor' => 'async',
+
+ 'processor' => 'async',
'processor.options' => array('usec' => 200000),
// Time zone. See: http://www.php.net/manual/en/timezones.php
'timezone' => 'UTC',
// Whitelist of plugins to load
'plugins' => array(
- // To enable a plugin, simply add a string to this array containing
- // the short name of the plugin as shown below.
+ // To enable a plugin, simply add a string to this array containing
+ // the short name of the plugin as shown below.
// 'ShortPluginName',
- // Below is an example of enabling the AutoJoin plugin, for which
- // the corresponding PEAR package is Phergie_Plugin_AutoJoin. This
- // plugin allows you to set a list of channels in this configuration
- // file that the bot will automatically join when it connects to a
- // server. If you'd like to enable this plugin, simply install it,
- // uncomment the line below, and set a value for the setting
- // autojoin.channels (examples for which are located further down in
+ // Below is an example of enabling the AutoJoin plugin, for which
+ // the corresponding PEAR package is Phergie_Plugin_AutoJoin. This
+ // plugin allows you to set a list of channels in this configuration
+ // file that the bot will automatically join when it connects to a
+ // server. If you'd like to enable this plugin, simply install it,
+ // uncomment the line below, and set a value for the setting
+ // autojoin.channels (examples for which are located further down in
// this file).
// 'AutoJoin',
// A few other recommended plugins:
- // Servers randomly send PING events to clients to ensure that
- // they're still connected and will eventually terminate the
-
- // connection if a PONG response is not received. The Pong plugin
+ // Servers randomly send PING events to clients to ensure that
+ // they're still connected and will eventually terminate the
+
+ // connection if a PONG response is not received. The Pong plugin
// handles sending these responses.
- // 'Pong',
+ // 'Pong',
- // It's sometimes difficult to distinguish between a lack of
- // activity on a server and the client not receiving data even
- // though a connection remains open. The Ping plugin performs a self
- // CTCP PING sporadically to ensure that its connection is still
+ // It's sometimes difficult to distinguish between a lack of
+ // activity on a server and the client not receiving data even
+ // though a connection remains open. The Ping plugin performs a self
+ // CTCP PING sporadically to ensure that its connection is still
// functioning and, if not, terminates the bot.
// 'Ping',
- // Sometimes it's desirable to have the bot disconnect gracefully
- // when issued a command to do so via a PRIVMSG event. The Quit
- // plugin implements this using the Command plugin to intercept the
+ // Sometimes it's desirable to have the bot disconnect gracefully
+ // when issued a command to do so via a PRIVMSG event. The Quit
+ // plugin implements this using the Command plugin to intercept the
// command.
// 'Quit',
),
- // If set to true, this allows any plugin dependencies for plugins
- // listed in the 'plugins' option to be loaded even if they are not
+ // If set to true, this allows any plugin dependencies for plugins
+ // listed in the 'plugins' option to be loaded even if they are not
// explicitly included in that list
'plugins.autoload' => true,
// Examples of setting values for Ping plugin settings
- // This is the amount of time in seconds that the Ping plugin will wait
- // to receive an event from the server before it initiates a self-ping
+ // This is the amount of time in seconds that the Ping plugin will wait
+ // to receive an event from the server before it initiates a self-ping
// 'ping.event' => 300, // 5 minutes
- // This is the amount of time in seconds that the Ping plugin will wait
- // following a self-ping attempt before it assumes that a response will
+ // This is the amount of time in seconds that the Ping plugin will wait
+ // following a self-ping attempt before it assumes that a response will
// never be received and terminates the connection
// 'ping.ping' => 10, // 10 seconds
*/
protected $handler;
+ /**
+ * Mock Phergie_Config instance passed to the plugin handler constructor
+ *
+ * @var Phergie_Config
+ */
+ protected $config;
+
+ /**
+ * Mock Phergie_Event_Handler instance passed to the plugin handler
+ * constructor
+ *
+ * @var Phergie_Event_Handler
+ */
+ protected $events;
+
+ /**
+ * Returns a mock plugin instance.
+ *
+ * @param string $name Optional short name for the mock plugin, defaults
+ * to 'TestPlugin'
+ * @param array $methods Optional list of methods to override
+ *
+ * @return Phergie_Plugin_Abstract
+ */
+ protected function getMockPlugin($name = 'TestPlugin', array $methods = array())
+ {
+ $methods[] = 'getName';
+ $plugin = $this->getMock('Phergie_Plugin_Abstract', $methods);
+ $plugin
+ ->expects($this->any())
+ ->method('getName')
+ ->will($this->returnValue($name));
+ return $plugin;
+ }
+
/**
* Sets up a new handler instance before each test.
*
*/
public function setUp()
{
+ $this->config = $this->getMock('Phergie_Config');
+ $this->events = $this->getMock('Phergie_Event_Handler');
$this->handler = new Phergie_Plugin_Handler(
- new Phergie_Config(),
- new Phergie_Event_Handler()
+ $this->config,
+ $this->events
);
}
/**
- * Destroys the handler instance after each test
+ * Tests iterability of the plugin handler.
*
* @return void
*/
- public function tearDown()
+ public function testImplementsIteratorAggregate()
{
- unset($this->handler);
+ $reflection = new ReflectionObject($this->handler);
+
+ $this->assertTrue(
+ $reflection->implementsInterface('IteratorAggregate'),
+ 'Handler does not implement IteratorAggregate'
+ );
+
+ $this->assertType(
+ 'Iterator',
+ $this->handler->getIterator(),
+ 'getIterator() must return an iterator'
+ );
}
/**
- * Ensures that we can iterate over the handler
+ * Tests countability of the plugin handler.
*
* @return void
*/
- public function testImplementsIterator()
+ public function testImplementsCountable()
{
$reflection = new ReflectionObject($this->handler);
+
$this->assertTrue(
- $reflection->implementsInterface('IteratorAggregate')
+ $reflection->implementsInterface('Countable'),
+ 'Handler does not implement Countable'
);
$this->assertType(
- 'Iterator', $this->handler->getIterator(),
- 'getIterator() must actually return an Iterator'
+ 'int',
+ count($this->handler),
+ 'count() must return an integer'
);
}
/**
- * Ensures a newly instantiated handler does not have plugins associated
- * with it
+ * Tests the plugin handler exposing added plugins as instance
+ * properties of the handler via isset().
*
- * @depends testImplementsIterator
* @return void
*/
- public function testEmptyHandlerHasNoPlugins()
+ public function testImplementsIsset()
+ {
+ $pluginName = 'TestPlugin';
+ $this->assertFalse(isset($this->handler->{$pluginName}));
+ $plugin = $this->getMockPlugin($pluginName);
+ $this->handler->addPlugin($plugin);
+ $this->assertTrue(isset($this->handler->{$pluginName}));
+ }
+
+ /**
+ * Tests the plugin handler exposing added plugins as instance
+ * properties of the handler.
+ *
+ * @depends testImplementsIsset
+ * @return void
+ */
+ public function testImplementsGet()
+ {
+ $plugin = $this->getMockPlugin();
+ $this->handler->addPlugin($plugin);
+ $name = $plugin->getName();
+ $getPlugin = $this->handler->getPlugin($name);
+ $this->assertTrue(isset($this->handler->$name));
+ $get = $this->handler->$name;
+ $this->assertSame($getPlugin, $get);
+ }
+
+ /**
+ * Tests the plugin handler allowing for plugin removal via unset().
+ *
+ * @depends testImplementsGet
+ * @return void
+ */
+ public function testImplementsUnset()
+ {
+ $plugin = $this->getMockPlugin();
+ $this->handler->addPlugin($plugin);
+ unset($this->handler->{$plugin->getName()});
+ $this->assertFalse($this->handler->hasPlugin($plugin->getName()));
+ }
+
+ /**
+ * Tests the plugin handler executing a callback on all contained
+ * plugins where one plugin short-circuits the process.
+ *
+ * @return void
+ */
+ public function testImplementsCallWithShortCircuit()
+ {
+ $plugin1 = $this->getMockPlugin('TestPlugin1', array('callback'));
+ $plugin1
+ ->expects($this->once())
+ ->method('callback')
+ ->will($this->returnValue(false));
+ $this->handler->addPlugin($plugin1);
+
+ $plugin2 = $this->getMockPlugin('TestPlugin2', array('callback'));
+ $plugin2
+ ->expects($this->exactly(0))
+ ->method('callback');
+ $this->handler->addPlugin($plugin2);
+
+ $this->assertFalse($this->handler->callback());
+ }
+
+ /**
+ * Tests the plugin handler executing a callback on all contained
+ * plugins where no plugins short-circuit the process.
+ *
+ * @return void
+ */
+ public function testImplementsCallWithoutShortCircuit()
{
- $count = 0;
- foreach ($this->handler as $plugin) {
- $count++;
+ foreach (range(1, 2) as $index) {
+ $plugin = $this->getMockPlugin('TestPlugin' . $index, array('callback'));
+ $plugin
+ ->expects($this->once())
+ ->method('callback');
+ $this->handler->addPlugin($plugin);
}
- $this->assertEquals(0, $count);
+ $this->assertTrue($this->handler->callback());
}
-
+
/**
- * Ensures a newly instantiated handler does not default to autoload
+ * Tests a newly instantiated handler not having plugins associated with
+ * it.
*
+ * @depends testImplementsCountable
* @return void
*/
- public function testDefaultsToNotAutoload()
+ public function testEmptyHandlerHasNoPlugins()
+ {
+ $this->assertEquals(0, count($this->handler));
+ }
+
+ /**
+ * Tests a newly instantiated handler not having autoloading enabled by
+ * default.
+ *
+ * @return void
+ */
+ public function testGetAutoloadDefaultsToNotAutoload()
{
$this->assertFalse($this->handler->getAutoload());
}
/**
- * addPath provides a fluent interface
+ * Tests setAutoload().
+ *
+ * @depends testGetAutoloadDefaultsToNotAutoload
+ * @return void
+ */
+ public function testSetAutoload()
+ {
+ $this->assertSame(
+ $this->handler->setAutoload(true),
+ $this->handler,
+ 'setAutoload() does not provide a fluent interface'
+ );
+
+ $this->assertTrue(
+ $this->handler->getAutoload(),
+ 'setAutoload() had no effect on getAutoload()'
+ );
+ }
+
+ /**
+ * Tests addPath() providing a fluent interface.
*
* @return void
*/
}
/**
- * addPath throws an exception when it cannot read the directory
+ * Tests addPath() throwing an exception when it cannot read the
+ * directory.
*
* @return void
*/
return;
}
- $this->fail('An expected exception has not been raised.');
+ $this->fail('An expected exception has not been raised');
}
/**
- * adds a path into the plugin handler and then ensures that files
- * in that location can be found
+ * Tests adding a path to the plugin handler.
*
* @return void
*/
public function testAddPath()
{
- $plugin_name = 'Mock';
+ $pluginName = 'Mock';
+
try {
- $this->handler->addPlugin($plugin_name);
+ $this->handler->addPlugin($pluginName);
} catch(Phergie_Plugin_Exception $e) {
$this->assertEquals(
Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND,
$e->getCode()
);
-
- $this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
-
- try {
- $this->handler->addPlugin($plugin_name);
- } catch(Phergie_Plugin_Exception $e) {
- $this->fail(
- 'After adding the directory, the plugin was still '
- . 'not found.'
- );
- }
-
- return;
}
- $this->fail(
- 'Before adding the directory, an expected exception '
- . 'was not raised'
- );
+ if (!isset($e)) {
+ $this->fail('Plugin loaded, path was already present');
+ }
+
+ $this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
+
+ try {
+ $this->handler->addPlugin($pluginName);
+ } catch(Phergie_Plugin_Exception $e) {
+ $this->fail('Added path, plugin still not found');
+ }
}
/**
- * addPlugin returns the plugin instance that was added
+ * Tests addPlugin() returning an added plugin instance.
*
* @return void
*/
- public function testAddPluginByInstanceReturnsPluginInstance() {
- $plugin = $this->getMock('Phergie_Plugin_Abstract');
- $plugin
- ->expects($this->any())
- ->method('getName')
- ->will($this->returnValue('TestPlugin'));
-
- $returned_plugin = $this->handler->addPlugin($plugin);
+ public function testAddPluginByInstanceReturnsPluginInstance()
+ {
+ $plugin = $this->getMockPlugin();
+ $returnedPlugin = $this->handler->addPlugin($plugin);
$this->assertSame(
- $returned_plugin,
+ $returnedPlugin,
$plugin,
- 'addPlugin returns the same instance that is passed to it'
+ 'addPlugin() does not return the instance passed to it'
);
}
/**
- * Can add a plugin to the handler by shortname
+ * Tests adding a plugin to the handler using the plugin's short name.
*
* @return void
*/
- public function testAddPluginToHandlerByShortname()
+ public function testAddPluginByShortName()
{
- $plugin_name = 'Mock';
+ $pluginName = 'Mock';
$this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
- $returned_plugin = $this->handler->addPlugin($plugin_name);
- $this->assertTrue($this->handler->hasPlugin($plugin_name));
+ $returnedPlugin = $this->handler->addPlugin($pluginName);
+ $this->assertTrue($this->handler->hasPlugin($pluginName));
+
$this->assertType(
'Phergie_Plugin_Mock',
- $this->handler->getPlugin($plugin_name)
+ $this->handler->getPlugin($pluginName)
);
+
$this->assertSame(
- $this->handler->getPlugin($plugin_name),
- $returned_plugin,
- 'Handler contains plugin when added by shortname.'
+ $this->handler->getPlugin($pluginName),
+ $returnedPlugin,
+ 'Handler does not contain added plugin'
);
}
/**
- * Can add a plugin to the handler by instance
+ * Tests adding a plugin instance to the handler.
*
* @return void
*/
- public function testAddPluginToHandlerByInstance()
+ public function testAddPluginByInstance()
{
- $plugin = $this->getMock('Phergie_Plugin_Abstract');
- $plugin
- ->expects($this->any())
- ->method('getName')
- ->will($this->returnValue('TestPlugin'));
-
- $returned_plugin = $this->handler->addPlugin($plugin);
-
+ $plugin = $this->getMockPlugin();
+ $returnedPlugin = $this->handler->addPlugin($plugin);
$this->assertTrue($this->handler->hasPlugin('TestPlugin'));
+
$this->assertSame(
- $plugin, $returned_plugin,
- 'addPlugin returns the same plugin'
+ $plugin,
+ $returnedPlugin,
+ 'addPlugin() does not return added plugin instance'
);
+
$this->assertSame(
- $plugin, $this->handler->getPlugin('TestPlugin'),
- 'getPlugin returns the same plugin'
+ $plugin,
+ $this->handler->getPlugin('TestPlugin'),
+ 'getPlugin() does not return added plugin instance'
);
}
/**
- * addPlugin throws an exception when it can't find the plugin
+ * Tests addPlugin() throwing an exception when the plugin class file
+ * can't be found.
*
* @return void
*/
- public function testAddPluginThrowsExceptionIfCannotFindPlugin()
+ public function testAddPluginThrowsExceptionWhenPluginFileNotFound()
{
try {
$this->handler->addPlugin('TestPlugin');
return;
}
- $this->fail('An expected exception has not been raised.');
+ $this->fail('An expected exception has not been raised');
+ }
+
+ /**
+ * Recursively removes all files and subdirectories in a directory.
+ *
+ * @param string $path Directory path
+ * @return void
+ */
+ private function removeDirectory($path)
+ {
+ if (file_exists($path)) {
+ $it = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($path),
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
+ foreach ($it as $entry) {
+ if ($it->isDot()) {
+ continue;
+ }
+ if ($entry->isDir()) {
+ rmdir($entry->getPathname());
+ } else {
+ unlink($entry->getPathname());
+ }
+ }
+ }
+ }
+
+ /**
+ * Tests addPlugin() throwing an exception when the plugin class file is
+ * found, but does not contain the plugin class as expected.
+ *
+ * @return void
+ */
+ public function testAddPluginThrowsExceptionWhenPluginClassNotFound()
+ {
+ $path = sys_get_temp_dir() . '/Phergie/Plugin';
+ $this->removeDirectory(dirname($path));
+ mkdir($path, 0777, true);
+ touch($path . '/TestPlugin.php');
+ $this->handler->addPath($path, 'Phergie_Plugin_');
+
+ try {
+ $this->handler->addPlugin('TestPlugin');
+ } catch(Phergie_Plugin_Exception $e) { }
+
+ if (isset($e)) {
+ $this->assertEquals(
+ Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND,
+ $e->getCode()
+ );
+ } else {
+ $this->fail('An expected exception has not been raised');
+ }
+
+ $this->removeDirectory(dirname($path));
}
/**
- * addPlugin throws an exception when trying to instantiate a
- * class that doesn't extend from Phergie_Plugin_Abstract
+ * Tests addPlugin() throwing an exception when trying to instantiate a
+ * class that doesn't extend Phergie_Plugin_Abstract.
*
* @return void
*/
return;
}
- $this->fail('An expected exception has not been raised.');
+ $this->fail('An expected exception has not been raised');
}
/**
- * addPlugin throws an exception when trying to instantiate a
+ * Tests addPlugin() throwing an exception when trying to instantiate a
* class that can't be instantiated.
*
* @return void
return;
}
- $this->fail('An expected exception has not been raised.');
+ $this->fail('An expected exception has not been raised');
}
/**
- * addPlugin with shortname and arguments passes args to constructor
+ * Tests adding a plugin by its short name with arguments passed to the
+ * plugin constructor.
*
- * @return null
+ * @return void
*/
- public function testAddPluginShortnamePassesArgsToConstructor()
+ public function testAddPluginShortNamePassesArgsToConstructor()
{
- $plugin_name = 'Mock';
+ $pluginName = 'Mock';
$this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
$arguments = array('a', 'b', 'c');
+ $plugin = $this->handler->addPlugin($pluginName, $arguments);
- $plugin = $this->handler->addPlugin($plugin_name, $arguments);
$this->assertAttributeSame(
$arguments,
- 'args',
+ 'arguments',
$plugin,
- 'Arguments passed in to addPlugin match the arguments '
- . 'the Mock plugin constructor received'
+ 'Arguments do not match'
);
}
/**
- * addPlugin passes Phergie_Config to instantiated plugin
+ * Tests addPlugin() passing Phergie_Config to an instantiated plugin.
*
- * @return null
+ * @return void
*/
- public function testAddPluginPassesPhergieConfigToInstantiatedPlugin()
+ public function testAddPluginPassesConstructorArguments()
{
- $my_config = new Phergie_Config();
- $my_config['my_option'] = 'my_value';
-
- // create a new handler with this config
- unset($this->handler);
- $this->handler = new Phergie_Plugin_Handler(
- $my_config,
- new Phergie_Event_Handler()
- );
-
- $plugin_name = 'Mock';
+ $pluginName = 'Mock';
$this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
-
- $plugin = $this->handler->addPlugin($plugin_name);
+ $plugin = $this->handler->addPlugin($pluginName);
$this->assertSame(
- $my_config,
+ $this->config,
$plugin->getConfig(),
- 'addPlugin passes Phergie_Config to instantiated plugin'
+ 'Phergie_Config instances do not match'
);
- }
-
- /**
- * addPlugin passes Phergie_Event_Handler to instantiated plugin
- *
- * @return null
- */
- public function testAddPluginPassesPhergieEventHandlerToInstantiatedPlugin()
- {
- $plugin = $this->getMock('Phergie_Plugin_Abstract');
- $plugin
- ->expects($this->any())
- ->method('getName')
- ->will($this->returnValue('TestPlugin'));
-
- $my_event_handler = new Phergie_Event_Handler();
- $my_event_handler->addEvent($plugin, 'ping');
-
- // create a new plugin handler with this event handler
- unset($this->handler);
- $this->handler = new Phergie_Plugin_Handler(
- new Phergie_Config(),
- $my_event_handler
- );
-
- $plugin_name = 'Mock';
- $this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
-
- $plugin = $this->handler->addPlugin($plugin_name);
$this->assertSame(
- $my_event_handler,
+ $this->events,
$plugin->getEventHandler(),
- 'addPlugin passes Phergie_Event_Handler to instantiated plugin'
+ 'Phergie_Event_Handler instances do not match'
);
}
/**
- * @todo addPlugin calls onLoad() to instantiated plugin
- */
-
- /**
- * implements __isset
+ * Tests addPlugin() calling onLoad() on an instantiated plugin.
*
* @return void
*/
- public function testPluginHandlerImplementsIsset()
+ public function testAddPluginCallsOnLoadOnInstantiatedPlugin()
{
- $plugin_name = 'TestPlugin';
-
- $this->assertFalse(isset($this->handler->{$plugin_name}));
-
- $plugin = $this->getMock('Phergie_Plugin_Abstract');
+ $plugin = $this->getMockPlugin(null, array('onLoad'));
$plugin
- ->expects($this->any())
- ->method('getName')
- ->will($this->returnValue($plugin_name));
-
+ ->expects($this->once())
+ ->method('onLoad');
$this->handler->addPlugin($plugin);
-
- $this->assertTrue(isset($this->handler->{$plugin_name}));
-
}
/**
- * addPlugin() returns the same plugin when requested twice
+ * Tests addPlugin() returning the same plugin when called twice.
*
* @return void
*/
public function testAddPluginReturnsSamePluginWhenAskedTwice()
{
- $plugin_name = 'Mock';
+ $pluginName = 'Mock';
$this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
-
- $plugin1 = $this->handler->addPlugin($plugin_name);
- $plugin2 = $this->handler->addPlugin($plugin_name);
+ $plugin1 = $this->handler->addPlugin($pluginName);
+ $plugin2 = $this->handler->addPlugin($pluginName);
$this->assertSame($plugin1, $plugin2);
}
-
/**
- * Tests an exception is thrown when trying to get a plugin
- * that is not already loaded and autoload is off
+ * Tests getPlugin() throwing an exception when trying to get an
+ * unloaded plugin with autoload disabled.
*
- * @depends testDefaultsToNotAutoload
+ * @depends testGetAutoloadDefaultsToNotAutoload
* @return void
*/
public function testExceptionThrownWhenLoadingPluginWithoutAutoload()
return;
}
- $this->fail('An expected exception has not been raised.');
+ $this->fail('An expected exception has not been raised');
+ }
+
+ /**
+ * Tests addPlugins() with a plugin short name and no plugin constructor
+ * arguments.
+ *
+ * @depends testAddPluginByShortName
+ * @depends testAddPluginByInstance
+ * @return void
+ */
+ public function testAddPluginsWithoutArguments()
+ {
+ $prefix = 'Phergie_Plugin_';
+ $this->handler->addPath(dirname(__FILE__), $prefix);
+
+ $plugin = 'Mock';
+ $this->handler->addPlugins(array($plugin));
+ $returnedPlugin = $this->handler->getPlugin($plugin);
+ $this->assertContains(
+ get_class($returnedPlugin),
+ $prefix . $plugin,
+ 'Short name plugin not of expected class'
+ );
+ }
+
+ /**
+ * Tests addPlugins() with a plugin short name and plugin constructor
+ * arguments.
+ *
+ * @depends testAddPluginByShortName
+ * @depends testAddPluginByInstance
+ * @return void
+ */
+ public function testAddPluginsWithArguments()
+ {
+ $prefix = 'Phergie_Plugin_';
+ $this->handler->addPath(dirname(__FILE__), $prefix);
+
+ $arguments = array(1, 2, 3);
+ $plugin = array('Mock', $arguments);
+ $this->handler->addPlugins(array($plugin));
+ $returnedPlugin = $this->handler->getPlugin('Mock');
+ $this->assertEquals(
+ $arguments,
+ $returnedPlugin->getArguments(),
+ 'Constructor arguments for instance plugin do not match'
+ );
+ }
+
+ /**
+ * Tests removePlugin() with a plugin instance.
+ *
+ * @depends testAddPluginByInstance
+ * @return void
+ */
+ public function testRemovePluginByInstance()
+ {
+ $plugin = $this->getMockPlugin();
+ $this->handler->addPlugin($plugin);
+ $this->handler->removePlugin($plugin);
+ $this->assertFalse(
+ $this->handler->hasPlugin($plugin->getName()),
+ 'Plugin was not removed'
+ );
+ }
+
+ /**
+ * Tests removePlugin() with a plugin short name.
+ *
+ * @depends testAddPluginByShortName
+ * @return void
+ */
+ public function testRemovePluginByShortName()
+ {
+ $plugin = 'Mock';
+ $this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
+
+ $this->handler->addPlugin($plugin);
+ $this->handler->removePlugin($plugin);
+ $this->assertFalse(
+ $this->handler->hasPlugin($plugin),
+ 'Plugin was not removed'
+ );
+ }
+
+ /**
+ * Tests getPlugin() when the plugin is not already loaded and
+ * autoloading is disabled.
+ *
+ * @depends testSetAutoload
+ * @return void
+ */
+ public function testGetPluginWithAutoloadEnabled()
+ {
+ $this->handler->setAutoload(true);
+ $this->handler->addPath(dirname(__FILE__), 'Phergie_Plugin_');
+ $plugin = $this->handler->getPlugin('Mock');
+ $this->assertType(
+ 'Phergie_Plugin_Mock',
+ $plugin,
+ 'Retrieved plugin not of expected class'
+ );
+ }
+
+ /**
+ * Tests getPlugins().
+ *
+ * @depends testGetPluginWithAutoloadEnabled
+ * @return void
+ */
+ public function testGetPlugins()
+ {
+ $plugin1 = $this->getMockPlugin('TestPlugin1');
+ $this->handler->addPlugin($plugin1);
+
+ $plugin2 = $this->getMockPlugin('TestPlugin2');
+ $this->handler->addPlugin($plugin2);
+
+ $expected = array(
+ 'testplugin1' => $plugin1,
+ 'testplugin2' => $plugin2,
+ );
+
+ $actual = $this->handler->getPlugins();
+ $this->assertEquals($expected, $actual);
+
+ $actual = $this->handler->getPlugins(array('testplugin1', 'testplugin2'));
+ $this->assertEquals($expected, $actual);
}
}
/**
* Creates a plugin on the filesystem that can be used by
- * Phergie_Plugin_Handler's addPath utility to be located and loaded.
+ * Phergie_Plugin_Handler::addPath() to be located and loaded.
*
* @category Phergie
* @package Phergie_Tests
class Phergie_Plugin_Mock extends Phergie_Plugin_Abstract
{
/**
- * holds the arguments that were passed in to the constructor
+ * Arguments passed to the constructor
+ *
* @var array
*/
- protected $args;
+ protected $arguments;
/**
- * processes a variable number of arguments into the args property
+ * Stores all arguments for later use.
*
- * @return null
+ * @return void
*/
public function __construct()
{
- $this->args = func_get_args();
+ $this->arguments = func_get_args();
+ }
+
+ /**
+ * Returns all constructor arguments.
+ *
+ * @return array Enumerated array containing the arguments passed to the
+ * constructor in order
+ */
+ public function getArguments()
+ {
+ return $this->arguments;
}
}
--- /dev/null
+<?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
+ */
+
+require_once dirname(__FILE__) . '/TestCase.php';
+
+/**
+ * Unit test suite for Pherge_Plugin_SpellCheck.
+ *
+ * @category Phergie
+ * @package Phergie_Tests
+ * @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_SpellCheckTest extends Phergie_Plugin_TestCase
+{
+
+ /**
+ * Current SpellCheck plugin instance
+ *
+ * @var Phergie_Plugin_SpellCheck
+ */
+ protected $spell;
+
+ /**
+ * Sets up the fixture, for example, opens a network connection.
+ * This method is called before a test is executed.
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ $this->config = array('spellcheck.lang' => 'en');
+
+ $this->spell = new Phergie_Plugin_SpellCheck();
+ $this->setPlugin(new Phergie_Plugin_Command());
+
+ $config = $this->plugin->getConfig();
+
+ $handler = new Phergie_Plugin_Handler($config, $this->handler);
+ $this->plugin->setPluginHandler($handler);
+
+ $handler->addPlugin($this->plugin);
+ $handler->addPlugin($this->spell);
+
+ $this->spell->setEventHandler($this->handler);
+ $this->spell->setConnection($this->connection);
+ }
+
+ /**
+ * @event Phergie_Event_Request::privmsg
+ * @eventArg #zftalk
+ * @eventArg spell
+ */
+ public function testSpell()
+ {
+ $this->spell->onLoad();
+
+ $this->copyEvent();
+ $this->plugin->onPrivMsg();
+ $this->assertDoesNotHaveEvent(Phergie_Event_Command::TYPE_PRIVMSG);
+ }
+
+ /**
+ * @event Phergie_Event_Request::privmsg
+ * @eventArg #phergie
+ * @eventArg spell test
+ */
+ public function testSpellTest()
+ {
+ $this->spell->onLoad();
+
+ $this->copyEvent();
+ $this->plugin->onPrivMsg();
+
+ $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
+
+ $this->assertEquals(1, count($events));
+ foreach ($events as $event) {
+ $args = $event->getArguments();
+
+ $this->assertEquals('#phergie', $args[0]);
+
+ $this->assertContains('CheckSpellUser:', $args[1]);
+ $this->assertContains('test', $args[1]);
+ $this->assertContains('correct', $args[1]);
+ }
+ }
+
+ /**
+ * @event Phergie_Event_Request::privmsg
+ * @eventArg #phergie
+ * @eventArg spell testz
+ */
+ public function testSpellTestz()
+ {
+ $this->spell->onLoad();
+
+ $this->copyEvent();
+ $this->plugin->onPrivMsg();
+
+ $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
+
+ $this->assertEquals(1, count($events));
+ foreach ($events as $event) {
+ $args = $event->getArguments();
+
+ $this->assertEquals('#phergie', $args[0]);
+
+ $this->assertContains('CheckSpellUser:', $args[1]);
+ $this->assertRegExp('/([a-z]+, ){4}/', $args[1]);
+ $this->assertContains('testz', $args[1]);
+ $this->assertContains('test,', $args[1]);
+ }
+ }
+
+ /**
+ * @event Phergie_Event_Request::privmsg
+ * @eventArg #phergie
+ * @eventArg spell testz
+ */
+ public function testSpellMoreSuggestions()
+ {
+ $config = $this->spell->getConfig();
+
+ $this->copyEvent();
+ $config['spellcheck.limit'] = 6;
+
+ $this->spell->onLoad();
+ $this->plugin->onPrivMsg();
+
+ $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
+
+ $this->assertEquals(1, count($events));
+ foreach ($events as $event) {
+ $args = $event->getArguments();
+
+ $this->assertEquals('#phergie', $args[0]);
+
+ $this->assertContains('CheckSpellUser:', $args[1]);
+ $this->assertRegExp('/([a-z]+, ){5}/', $args[1]);
+ $this->assertContains('testz', $args[1]);
+ $this->assertContains('test,', $args[1]);
+ }
+ }
+
+ /**
+ * @event Phergie_Event_Request::privmsg
+ * @eventArg #phergie
+ * @eventArg spell qwertyuiopasdfghjklzxcvbnm
+ */
+ public function testSpellNoSuggestions()
+ {
+ $this->spell->onLoad();
+
+ $this->copyEvent();
+ $this->plugin->onPrivMsg();
+
+ $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
+
+ $this->assertEquals(1, count($events));
+ foreach ($events as $event) {
+ $args = $event->getArguments();
+
+ $this->assertEquals('#phergie', $args[0]);
+
+ $this->assertContains('CheckSpellUser:', $args[1]);
+ $this->assertContains('find any suggestions', $args[1]);
+ }
+ }
+
+ /**
+ * Copy event from command to spell plugin
+ *
+ * @return void
+ */
+ protected function copyEvent()
+ {
+ $hostmask = Phergie_Hostmask::fromString('CheckSpellUser!test@testing.org');
+
+ $event = $this->plugin->getEvent();
+ $event->setHostmask($hostmask);
+
+ $this->spell->setEvent($event);
+ }
+
+}
* http://phergie.org/license
*
* @category Phergie
- * @package Phergie_Tests
+ * @package Phergie_Tests
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
* @license http://phergie.org/license New BSD License
<include name="Phergie/Connection/Exception.php" />
<include name="Phergie/Connection/Handler.php" />
<include name="Phergie/Connection.php" />
+ <include name="Phergie/Db/Exception.php" />
+ <include name="Phergie/Db/Manager.php" />
+ <include name="Phergie/Db/Sqlite.php" />
<include name="Phergie/Driver/Abstract.php" />
<include name="Phergie/Driver/Exception.php" />
<include name="Phergie/Driver/Streams.php" />