<?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()
{
+ // Check the socket is still active
+ if (feof($this->socket)) {
+ throw new Phergie_Driver_Exception(
+ 'EOF detected on socket',
+ Phergie_Driver_Exception::ERR_CONNECTION_READ_FAILED
+ );
+ }
+
// Check for a new event on the current connection
$buffer = fgets($this->socket, 512);
// 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);
+ } else {
+ $hostmask = new Phergie_Hostmask(null, null, $prefix);
}
}
case 'privmsg':
case 'notice':
- $ctcp = substr(strstr($args, ':'), 1);
- if (substr($ctcp, 0, 1) === "\x01" && substr($ctcp, -1) === "\x01") {
+ $args = $this->parseArguments($args, 2);
+ list($source, $ctcp) = $args;
+ if (substr($ctcp, 0, 1) === "\001" && substr($ctcp, -1) === "\001") {
$ctcp = substr($ctcp, 1, -1);
$reply = ($cmd == 'notice');
list($cmd, $args) = array_pad(explode(' ', $ctcp, 2), 2, null);
}
break;
case 'action':
- $args = array($this->getConnection()->getNick(), $args);
+ $args = array($source, $args);
break;
default:
if ($reply) {
$cmd .= 'Response';
}
- $args = array($this->getConnection()->getNick(), $ctcp);
+ $args = array($source, $args);
break;
}
- } else {
- $args = $this->parseArguments($args, 2);
}
break;
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
*/