forked from kevinowino869/mitrobill
PHPMixBill v5.0 - First Upload
This commit is contained in:
36
system/autoload/PEAR2/Net/Transmitter/Exception.php
Normal file
36
system/autoload/PEAR2/Net/Transmitter/Exception.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* Generic exception class of this package.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
interface Exception
|
||||
{
|
||||
}
|
229
system/autoload/PEAR2/Net/Transmitter/FilterCollection.php
Normal file
229
system/autoload/PEAR2/Net/Transmitter/FilterCollection.php
Normal file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* A filter collection.
|
||||
*
|
||||
* Represents a collection of stream filters.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
* @see Client
|
||||
*/
|
||||
class FilterCollection implements \SeekableIterator, \Countable
|
||||
{
|
||||
/**
|
||||
* @var array The filter collection itself.
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* @var int A pointer, as required by SeekableIterator.
|
||||
*/
|
||||
protected $position = 0;
|
||||
|
||||
/**
|
||||
* Appends a filter to the collection
|
||||
*
|
||||
* @param string $name The name of the filter.
|
||||
* @param array $params An array of parameters for the filter.
|
||||
*
|
||||
* @return $this The collection itself.
|
||||
*/
|
||||
public function append($name, array $params = array())
|
||||
{
|
||||
$this->filters[] = array((string) $name, $params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the filter before a position.
|
||||
*
|
||||
* Inserts the specified filter before a filter at a specified position. The
|
||||
* new filter takes the specified position, while previous filters are moved
|
||||
* forward by one.
|
||||
*
|
||||
* @param int $position The position before which the filter will be
|
||||
* inserted.
|
||||
* @param string $name The name of the filter.
|
||||
* @param array $params An array of parameters for the filter.
|
||||
*
|
||||
* @return $this The collection itself.
|
||||
*/
|
||||
public function insertBefore($position, $name, array $params = array())
|
||||
{
|
||||
$position = (int) $position;
|
||||
if ($position <= 0) {
|
||||
$this->filters = array_merge(
|
||||
array(0 => array((string) $name, $params)),
|
||||
$this->filters
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
if ($position > count($this->filters)) {
|
||||
return $this->append($name, $params);
|
||||
}
|
||||
$this->filters = array_merge(
|
||||
array_slice($this->filters, 0, $position),
|
||||
array(0 => array((string) $name, $params)),
|
||||
array_slice($this->filters, $position)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a filter at a specified position.
|
||||
*
|
||||
* @param int $position The position from which to remove a filter.
|
||||
*
|
||||
* @return $this The collection itself.
|
||||
*/
|
||||
public function removeAt($position)
|
||||
{
|
||||
unset($this->filters[$position]);
|
||||
$this->filters = array_values($this->filters);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the collection
|
||||
*
|
||||
* @return $this The collection itself.
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->filters = array();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of filters in the collection.
|
||||
*
|
||||
* @return int The number of filters in the collection.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the pointer to 0.
|
||||
*
|
||||
* @return bool TRUE if the collection is not empty, FALSE otherwise.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
return $this->seek(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer to a specified position.
|
||||
*
|
||||
* @param int $position The position to move to.
|
||||
*
|
||||
* @return bool TRUE if the specified position is valid, FALSE otherwise.
|
||||
*/
|
||||
public function seek($position)
|
||||
{
|
||||
$this->position = $position;
|
||||
return $this->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current position.
|
||||
*
|
||||
* @return int The current position.
|
||||
*/
|
||||
public function getCurrentPosition()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer forward by 1.
|
||||
*
|
||||
* @return bool TRUE if the new position is valid, FALSE otherwise.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->position;
|
||||
return $this->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filter name at the current pointer position.
|
||||
*
|
||||
* @return string The name of the filter at the current position.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->valid() ? $this->filters[$this->position][0] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filter parameters at the current pointer position.
|
||||
*
|
||||
* @return array An array of parameters for the filter at the current
|
||||
* position.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->valid() ? $this->filters[$this->position][1] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer backwards by 1.
|
||||
*
|
||||
* @return bool TRUE if the new position is valid, FALSE otherwise.
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
--$this->position;
|
||||
return $this->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer to the last valid position.
|
||||
*
|
||||
* @return bool TRUE if the collection is not empty, FALSE otherwise.
|
||||
*/
|
||||
public function end()
|
||||
{
|
||||
$this->position = count($this->filters) - 1;
|
||||
return $this->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pointer is still pointing to an existing offset.
|
||||
*
|
||||
* @return bool TRUE if the pointer is valid, FALSE otherwise.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return array_key_exists($this->position, $this->filters);
|
||||
}
|
||||
}
|
36
system/autoload/PEAR2/Net/Transmitter/LockException.php
Normal file
36
system/autoload/PEAR2/Net/Transmitter/LockException.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* Exception thrown when something goes wrong when dealing with locks.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
class LockException extends \RuntimeException implements Exception
|
||||
{
|
||||
}
|
181
system/autoload/PEAR2/Net/Transmitter/NetworkStream.php
Normal file
181
system/autoload/PEAR2/Net/Transmitter/NetworkStream.php
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* A network transmitter.
|
||||
*
|
||||
* This is a convinience wrapper for network streams. Used to ensure data
|
||||
* integrity.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
abstract class NetworkStream extends Stream
|
||||
{
|
||||
/**
|
||||
* Used in {@link setCrypto()} to disable encryption.
|
||||
*/
|
||||
const CRYPTO_OFF = '';
|
||||
|
||||
/**
|
||||
* Used in {@link setCrypto()} to set encryption to either SSLv2 or SSLv3,
|
||||
* depending on what the other end supports.
|
||||
*/
|
||||
const CRYPTO_SSL = 'SSLv23';
|
||||
|
||||
/**
|
||||
* Used in {@link setCrypto()} to set encryption to SSLv2.
|
||||
*/
|
||||
const CRYPTO_SSL2 = 'SSLv2';
|
||||
|
||||
/**
|
||||
* Used in {@link setCrypto()} to set encryption to SSLv3.
|
||||
*/
|
||||
const CRYPTO_SSL3 = 'SSLv3';
|
||||
|
||||
/**
|
||||
* Used in {@link setCrypto()} to set encryption to TLS (exact version
|
||||
* negotiated between 1.0 and 1.2).
|
||||
*/
|
||||
const CRYPTO_TLS = 'TLS';
|
||||
|
||||
/**
|
||||
* @var string The type of stream. Can be either "_CLIENT" or "_SERVER".
|
||||
* Used to complement the encryption type. Must be set by child classes
|
||||
* for {@link setCrypto()} to work properly.
|
||||
*/
|
||||
protected $streamType = '';
|
||||
|
||||
/**
|
||||
* @var string The current cryptography setting.
|
||||
*/
|
||||
protected $crypto = '';
|
||||
|
||||
/**
|
||||
* Wraps around the specified stream.
|
||||
*
|
||||
* @param resource $stream The stream to wrap around.
|
||||
*/
|
||||
public function __construct($stream)
|
||||
{
|
||||
parent::__construct($stream, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current cryptography setting.
|
||||
*
|
||||
* @return string One of this class' CRYPTO_* constants.
|
||||
*/
|
||||
public function getCrypto()
|
||||
{
|
||||
return $this->crypto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current connection's cryptography setting.
|
||||
*
|
||||
* @param string $type The encryption type to set. Must be one of this
|
||||
* class' CRYPTO_* constants.
|
||||
*
|
||||
* @return boolean TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setCrypto($type)
|
||||
{
|
||||
if (self::CRYPTO_OFF === $type) {
|
||||
$result = stream_socket_enable_crypto($this->stream, false);
|
||||
} else {
|
||||
$result = stream_socket_enable_crypto(
|
||||
$this->stream,
|
||||
true,
|
||||
constant("STREAM_CRYPTO_METHOD_{$type}{$this->streamType}")
|
||||
);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$this->crypto = $type;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the stream is available for operations.
|
||||
*
|
||||
* @return bool TRUE if the stream is available, FALSE otherwise.
|
||||
*/
|
||||
public function isAvailable()
|
||||
{
|
||||
if (parent::isStream($this->stream)) {
|
||||
if ($this->isBlocking && feof($this->stream)) {
|
||||
return false;
|
||||
}
|
||||
$meta = stream_get_meta_data($this->stream);
|
||||
return !$meta['eof'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of a stream's buffer.
|
||||
*
|
||||
* @param int $size The desired size of the buffer, in bytes.
|
||||
* @param string $direction The buffer of which direction to set. Valid
|
||||
* values are the DIRECTION_* constants.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setBuffer($size, $direction = self::DIRECTION_ALL)
|
||||
{
|
||||
$result = parent::setBuffer($size, $direction);
|
||||
if (self::DIRECTION_SEND === $direction
|
||||
&& function_exists('stream_set_chunk_size') && !$result
|
||||
) {
|
||||
return false !== @stream_set_chunk_size($this->stream, $size);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown a full-duplex connection
|
||||
*
|
||||
* Shutdowns (partially or not) a full-duplex connection.
|
||||
*
|
||||
* @param string $direction The direction for which to disable further
|
||||
* communications.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function shutdown($direction = self::DIRECTION_ALL)
|
||||
{
|
||||
$directionMap = array(
|
||||
self::DIRECTION_ALL => STREAM_SHUT_RDWR,
|
||||
self::DIRECTION_SEND => STREAM_SHUT_WR,
|
||||
self::DIRECTION_RECEIVE => STREAM_SHUT_RD
|
||||
);
|
||||
return array_key_exists($direction, $directionMap)
|
||||
&& stream_socket_shutdown($this->stream, $directionMap[$direction]);
|
||||
}
|
||||
}
|
124
system/autoload/PEAR2/Net/Transmitter/SocketException.php
Normal file
124
system/autoload/PEAR2/Net/Transmitter/SocketException.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* Used to enable any exception in chaining.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* Exception thrown when something goes wrong with the connection.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
class SocketException extends StreamException
|
||||
{
|
||||
|
||||
/**
|
||||
* @var int The system level error code.
|
||||
*/
|
||||
protected $errorNo;
|
||||
|
||||
/**
|
||||
* @var string The system level error message.
|
||||
*/
|
||||
protected $errorStr;
|
||||
|
||||
/**
|
||||
* Creates a new socket exception.
|
||||
*
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param int $code The Exception code.
|
||||
* @param E|null $previous Previous exception thrown,
|
||||
* or NULL if there is none.
|
||||
* @param int|string|resource|null $fragment The fragment up until the
|
||||
* point of failure.
|
||||
* On failure with sending, this is the number of bytes sent
|
||||
* successfully before the failure.
|
||||
* On failure when receiving, this is a string/stream holding
|
||||
* the contents received successfully before the failure.
|
||||
* NULL if the failure occured before the operation started.
|
||||
* @param int $errorNo The system level error number.
|
||||
* @param string $errorStr The system level
|
||||
* error message.
|
||||
*/
|
||||
public function __construct(
|
||||
$message = '',
|
||||
$code = 0,
|
||||
E $previous = null,
|
||||
$fragment = null,
|
||||
$errorNo = null,
|
||||
$errorStr = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous, $fragment);
|
||||
$this->errorNo = $errorNo;
|
||||
$this->errorStr = $errorStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system level error code on the socket.
|
||||
*
|
||||
* @return int The system level error number.
|
||||
*/
|
||||
public function getSocketErrorNumber()
|
||||
{
|
||||
return $this->errorNo;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// Unreliable in testing.
|
||||
|
||||
/**
|
||||
* Gets the system level error message on the socket.
|
||||
*
|
||||
* @return string The system level error message.
|
||||
*/
|
||||
public function getSocketErrorMessage()
|
||||
{
|
||||
return $this->errorStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the exception.
|
||||
*
|
||||
* @return string The exception as a string.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if (null !== $this->getSocketErrorNumber()) {
|
||||
$result .= "\nSocket error number:" . $this->getSocketErrorNumber();
|
||||
}
|
||||
if (null !== $this->getSocketErrorMessage()) {
|
||||
$result .= "\nSocket error message:"
|
||||
. $this->getSocketErrorMessage();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
605
system/autoload/PEAR2/Net/Transmitter/Stream.php
Normal file
605
system/autoload/PEAR2/Net/Transmitter/Stream.php
Normal file
@ -0,0 +1,605 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* A stream transmitter.
|
||||
*
|
||||
* This is a convinience wrapper for stream functionality. Used to ensure data
|
||||
* integrity. Designed for TCP sockets, but it has intentionally been made to
|
||||
* accept any stream.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
class Stream
|
||||
{
|
||||
/**
|
||||
* Used to stop settings in either direction being applied.
|
||||
*/
|
||||
const DIRECTION_NONE = 0;
|
||||
/**
|
||||
* Used to apply settings only to receiving.
|
||||
*/
|
||||
const DIRECTION_RECEIVE = 1;
|
||||
/**
|
||||
* Used to apply settings only to sending.
|
||||
*/
|
||||
const DIRECTION_SEND = 2;
|
||||
/**
|
||||
* Used to apply settings to both sending and receiving.
|
||||
*/
|
||||
const DIRECTION_ALL = 3;
|
||||
|
||||
/**
|
||||
* @var resource The stream to wrap around.
|
||||
*/
|
||||
protected $stream;
|
||||
|
||||
/**
|
||||
* @var bool Whether to automaticaly close the stream on
|
||||
* object destruction if it's not a persistent one. Setting this to
|
||||
* FALSE may be useful if you're only using this class "part time",
|
||||
* while setting it to TRUE might be useful if you're doing some
|
||||
* "on offs".
|
||||
*/
|
||||
protected $autoClose = false;
|
||||
|
||||
/**
|
||||
* @var bool A flag that tells whether or not the stream is persistent.
|
||||
*/
|
||||
protected $persist;
|
||||
|
||||
/**
|
||||
* @var bool Whether the wrapped stream is in blocking mode or not.
|
||||
*/
|
||||
protected $isBlocking = true;
|
||||
|
||||
/**
|
||||
* @var array An associative array with the chunk size of each direction.
|
||||
* Key is the direction, value is the size in bytes as integer.
|
||||
*/
|
||||
protected $chunkSize = array(
|
||||
self::DIRECTION_SEND => 0xFFFFF, self::DIRECTION_RECEIVE => 0xFFFFF
|
||||
);
|
||||
|
||||
/**
|
||||
* Wraps around the specified stream.
|
||||
*
|
||||
* @param resource $stream The stream to wrap around.
|
||||
* @param bool $autoClose Whether to automaticaly close the stream on
|
||||
* object destruction if it's not a persistent one. Setting this to
|
||||
* FALSE may be useful if you're only using this class "part time",
|
||||
* while setting it to TRUE might be useful if you're doing some
|
||||
* "on offs".
|
||||
*
|
||||
* @see static::isFresh()
|
||||
*/
|
||||
public function __construct($stream, $autoClose = false)
|
||||
{
|
||||
if (!self::isStream($stream)) {
|
||||
throw $this->createException('Invalid stream supplied.', 1);
|
||||
}
|
||||
$this->stream = $stream;
|
||||
$this->autoClose = (bool) $autoClose;
|
||||
$this->persist = (bool) preg_match(
|
||||
'#\s?persistent\s?#sm',
|
||||
get_resource_type($stream)
|
||||
);
|
||||
$meta = stream_get_meta_data($stream);
|
||||
$this->isBlocking = isset($meta['blocked']) ? $meta['blocked'] : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP error handler for connection errors.
|
||||
*
|
||||
* @param string $level Level of PHP error raised. Ignored.
|
||||
* @param string $message Message raised by PHP.
|
||||
*
|
||||
* @return void
|
||||
* @throws SocketException That's how the error is handled.
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
protected function handleError($level, $message)
|
||||
{
|
||||
throw $this->createException($message, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given variable is a stream resource.
|
||||
*
|
||||
* @param mixed $var The variable to check.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function isStream($var)
|
||||
{
|
||||
return is_resource($var)
|
||||
&& (bool) preg_match('#\s?stream$#sm', get_resource_type($var));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the wrapped stream is fresh.
|
||||
*
|
||||
* Checks whether the wrapped stream is fresh. A stream is considered fresh
|
||||
* if there hasn't been any activity on it. Particularly useful for
|
||||
* detecting reused persistent connections.
|
||||
*
|
||||
* @return bool TRUE if the socket is fresh, FALSE otherwise.
|
||||
*/
|
||||
public function isFresh()
|
||||
{
|
||||
return ftell($this->stream) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the wrapped stream is a persistent one.
|
||||
*
|
||||
* @return bool TRUE if the stream is a persistent one, FALSE otherwise.
|
||||
*/
|
||||
public function isPersistent()
|
||||
{
|
||||
return $this->persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the wrapped stream is a blocking one.
|
||||
*
|
||||
* @return bool TRUE if the stream is a blocking one, FALSE otherwise.
|
||||
*/
|
||||
public function isBlocking()
|
||||
{
|
||||
return $this->isBlocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets blocking mode.
|
||||
*
|
||||
* @param bool $block Sets whether the stream is in blocking mode.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setIsBlocking($block)
|
||||
{
|
||||
$block = (bool)$block;
|
||||
if (stream_set_blocking($this->stream, (int)$block)) {
|
||||
$this->isBlocking = $block;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeout for the stream.
|
||||
*
|
||||
* @param int $seconds Timeout in seconds.
|
||||
* @param int $microseconds Timeout in microseconds to be added to the
|
||||
* seconds.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setTimeout($seconds, $microseconds = 0)
|
||||
{
|
||||
return stream_set_timeout($this->stream, $seconds, $microseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of a stream's buffer.
|
||||
*
|
||||
* @param int $size The desired size of the buffer, in bytes.
|
||||
* @param string $direction The buffer of which direction to set. Valid
|
||||
* values are the DIRECTION_* constants.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setBuffer($size, $direction = self::DIRECTION_ALL)
|
||||
{
|
||||
switch($direction) {
|
||||
case self::DIRECTION_SEND:
|
||||
return stream_set_write_buffer($this->stream, $size) === 0;
|
||||
case self::DIRECTION_RECEIVE:
|
||||
return stream_set_read_buffer($this->stream, $size) === 0;
|
||||
case self::DIRECTION_ALL:
|
||||
return $this->setBuffer($size, self::DIRECTION_RECEIVE)
|
||||
&& $this->setBuffer($size, self::DIRECTION_SEND);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the chunk.
|
||||
*
|
||||
* To ensure data integrity, as well as to allow for lower memory
|
||||
* consumption, data is sent/received in chunks. This function
|
||||
* allows you to set the size of each chunk. The default is 0xFFFFF.
|
||||
*
|
||||
* @param int $size The desired size of the chunk, in bytes.
|
||||
* @param string $direction The chunk of which direction to set. Valid
|
||||
* values are the DIRECTION_* constants.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setChunk($size, $direction = self::DIRECTION_ALL)
|
||||
{
|
||||
$size = (int) $size;
|
||||
if ($size <= 0) {
|
||||
return false;
|
||||
}
|
||||
switch($direction) {
|
||||
case self::DIRECTION_SEND:
|
||||
case self::DIRECTION_RECEIVE:
|
||||
$this->chunkSize[$direction] = $size;
|
||||
return true;
|
||||
case self::DIRECTION_ALL:
|
||||
$this->chunkSize[self::DIRECTION_SEND]
|
||||
= $this->chunkSize[self::DIRECTION_RECEIVE] = $size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the chunk.
|
||||
*
|
||||
* @param string $direction The chunk of which direction to get. Valid
|
||||
* values are the DIRECTION_* constants.
|
||||
*
|
||||
* @return int|array|false The chunk size in bytes,
|
||||
* or an array of chunk sizes with the directions as keys.
|
||||
* FALSE on invalid direction.
|
||||
*/
|
||||
public function getChunk($direction = self::DIRECTION_ALL)
|
||||
{
|
||||
switch($direction) {
|
||||
case self::DIRECTION_SEND:
|
||||
case self::DIRECTION_RECEIVE:
|
||||
return $this->chunkSize[$direction];
|
||||
case self::DIRECTION_ALL:
|
||||
return $this->chunkSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a string or stream over the wrapped stream.
|
||||
*
|
||||
* Sends a string or stream over the wrapped stream. If a seekable stream is
|
||||
* provided, it will be seeked back to the same position it was passed as,
|
||||
* regardless of the $offset parameter.
|
||||
*
|
||||
* @param string|resource $contents The string or stream to send.
|
||||
* @param int $offset The offset from which to start sending.
|
||||
* If a stream is provided, and this is set to NULL, sending will start
|
||||
* from the current stream position.
|
||||
* @param int $length The maximum length to send. If omitted,
|
||||
* the string/stream will be sent to its end.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
*/
|
||||
public function send($contents, $offset = null, $length = null)
|
||||
{
|
||||
$bytes = 0;
|
||||
$chunkSize = $this->chunkSize[self::DIRECTION_SEND];
|
||||
$lengthIsNotNull = null !== $length;
|
||||
$offsetIsNotNull = null !== $offset;
|
||||
if (self::isStream($contents)) {
|
||||
if ($offsetIsNotNull) {
|
||||
$oldPos = ftell($contents);
|
||||
fseek($contents, $offset, SEEK_SET);
|
||||
}
|
||||
while (!feof($contents)) {
|
||||
if ($lengthIsNotNull
|
||||
&& 0 === $chunkSize = min($chunkSize, $length - $bytes)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
$bytesNow = @fwrite(
|
||||
$this->stream,
|
||||
fread($contents, $chunkSize)
|
||||
);
|
||||
if (0 != $bytesNow) {
|
||||
$bytes += $bytesNow;
|
||||
} elseif ($this->isBlocking || false === $bytesNow) {
|
||||
throw $this->createException(
|
||||
'Failed while sending stream.',
|
||||
2,
|
||||
null,
|
||||
$bytes
|
||||
);
|
||||
} else {
|
||||
usleep(300000);
|
||||
}
|
||||
$this->isAcceptingData(null);
|
||||
}
|
||||
if ($offsetIsNotNull) {
|
||||
fseek($contents, $oldPos, SEEK_SET);
|
||||
} else {
|
||||
fseek($contents, -$bytes, SEEK_CUR);
|
||||
}
|
||||
} else {
|
||||
$contents = (string) $contents;
|
||||
if ($offsetIsNotNull) {
|
||||
$contents = substr($contents, $offset);
|
||||
}
|
||||
if ($lengthIsNotNull) {
|
||||
$contents = substr($contents, 0, $length);
|
||||
}
|
||||
$bytesToSend = (double) sprintf('%u', strlen($contents));
|
||||
while ($bytes < $bytesToSend) {
|
||||
$bytesNow = @fwrite(
|
||||
$this->stream,
|
||||
substr($contents, $bytes, $chunkSize)
|
||||
);
|
||||
if (0 != $bytesNow) {
|
||||
$bytes += $bytesNow;
|
||||
} elseif ($this->isBlocking || false === $bytesNow) {
|
||||
throw $this->createException(
|
||||
'Failed while sending string.',
|
||||
3,
|
||||
null,
|
||||
$bytes
|
||||
);
|
||||
} else {
|
||||
usleep(300000);
|
||||
}
|
||||
$this->isAcceptingData(null);
|
||||
}
|
||||
}
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the wrapped stream to receive.
|
||||
*
|
||||
* Reads from the wrapped stream to receive content as a string.
|
||||
*
|
||||
* @param int $length The number of bytes to receive.
|
||||
* @param string $what Descriptive string about what is being received
|
||||
* (used in exception messages).
|
||||
*
|
||||
* @return string The received content.
|
||||
*/
|
||||
public function receive($length, $what = 'data')
|
||||
{
|
||||
$result = '';
|
||||
$chunkSize = $this->chunkSize[self::DIRECTION_RECEIVE];
|
||||
while ($length > 0) {
|
||||
while ($this->isAvailable()) {
|
||||
$fragment = fread($this->stream, min($length, $chunkSize));
|
||||
if ('' != $fragment) {
|
||||
$length -= strlen($fragment);
|
||||
$result .= $fragment;
|
||||
continue 2;
|
||||
} elseif (!$this->isBlocking && !(false === $fragment)) {
|
||||
usleep(3000);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
throw $this->createException(
|
||||
"Failed while receiving {$what}",
|
||||
4,
|
||||
null,
|
||||
$result
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the wrapped stream to receive.
|
||||
*
|
||||
* Reads from the wrapped stream to receive content as a stream.
|
||||
*
|
||||
* @param int $length The number of bytes to receive.
|
||||
* @param FilterCollection $filters A collection of filters to apply to the
|
||||
* stream while receiving. Note that the filters will not be present on
|
||||
* the stream after receiving is done.
|
||||
* @param string $what Descriptive string about what is being
|
||||
* received (used in exception messages).
|
||||
*
|
||||
* @return resource The received content.
|
||||
*/
|
||||
public function receiveStream(
|
||||
$length,
|
||||
FilterCollection $filters = null,
|
||||
$what = 'stream data'
|
||||
) {
|
||||
$result = fopen('php://temp', 'r+b');
|
||||
$appliedFilters = array();
|
||||
if (null !== $filters) {
|
||||
foreach ($filters as $filtername => $params) {
|
||||
$appliedFilters[] = stream_filter_append(
|
||||
$result,
|
||||
$filtername,
|
||||
STREAM_FILTER_WRITE,
|
||||
$params
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$chunkSize = $this->chunkSize[self::DIRECTION_RECEIVE];
|
||||
while ($length > 0) {
|
||||
while ($this->isAvailable()) {
|
||||
$fragment = fread($this->stream, min($length, $chunkSize));
|
||||
if ('' != $fragment) {
|
||||
$length -= strlen($fragment);
|
||||
fwrite($result, $fragment);
|
||||
continue 2;
|
||||
} elseif (!$this->isBlocking && !(false === $fragment)) {
|
||||
usleep(3000);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($appliedFilters as $filter) {
|
||||
stream_filter_remove($filter);
|
||||
}
|
||||
rewind($result);
|
||||
throw $this->createException(
|
||||
"Failed while receiving {$what}",
|
||||
5,
|
||||
null,
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($appliedFilters as $filter) {
|
||||
stream_filter_remove($filter);
|
||||
}
|
||||
rewind($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the stream is available for operations.
|
||||
*
|
||||
* For network streams, this means whether the other end has closed the
|
||||
* connection.
|
||||
*
|
||||
* @return bool TRUE if the stream is available, FALSE otherwise.
|
||||
*/
|
||||
public function isAvailable()
|
||||
{
|
||||
return self::isStream($this->stream) && !feof($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there is data to be read from the wrapped stream.
|
||||
*
|
||||
* @param int|null $sTimeout If theere isn't data awaiting currently,
|
||||
* wait for it this many seconds for data to arrive. If NULL is
|
||||
* specified, wait indefinetly for that.
|
||||
* @param int $usTimeout Microseconds to add to the waiting time.
|
||||
*
|
||||
* @return bool TRUE if there is data to be read, FALSE otherwise.
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
*/
|
||||
public function isDataAwaiting($sTimeout = 0, $usTimeout = 0)
|
||||
{
|
||||
if (self::isStream($this->stream)) {
|
||||
if (null === $sTimeout && !$this->isBlocking) {
|
||||
$meta = stream_get_meta_data($this->stream);
|
||||
return !$meta['eof'];
|
||||
}
|
||||
|
||||
$w = $e = null;
|
||||
$r = array($this->stream);
|
||||
return 1 === @/* due to PHP bug #54563 */stream_select(
|
||||
$r,
|
||||
$w,
|
||||
$e,
|
||||
$sTimeout,
|
||||
$usTimeout
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the wrapped stream can be written to without a block.
|
||||
*
|
||||
* @param int|null $sTimeout If the stream isn't currently accepting data,
|
||||
* wait for it this many seconds to start accepting data. If NULL is
|
||||
* specified, wait indefinetly for that.
|
||||
* @param int $usTimeout Microseconds to add to the waiting time.
|
||||
*
|
||||
* @return bool TRUE if the wrapped stream would not block on a write, FALSE
|
||||
* otherwise.
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
*/
|
||||
public function isAcceptingData($sTimeout = 0, $usTimeout = 0)
|
||||
{
|
||||
if (self::isStream($this->stream)) {
|
||||
if (!$this->isBlocking) {
|
||||
$meta = stream_get_meta_data($this->stream);
|
||||
return !$meta['eof'];
|
||||
} elseif (feof($this->stream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$r = $e = null;
|
||||
$w = array($this->stream);
|
||||
return 1 === @/* due to PHP bug #54563 */stream_select(
|
||||
$r,
|
||||
$w,
|
||||
$e,
|
||||
$sTimeout,
|
||||
$usTimeout
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the opened stream, unless it's a persistent one.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ((!$this->persist) && $this->autoClose) {
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the opened stream, even if it is a persistent one.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return self::isStream($this->stream) && fclose($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*
|
||||
* Creates a new exception. Used by the rest of the functions in this class.
|
||||
* Override in derived classes for custom exception handling.
|
||||
*
|
||||
* @param string $message The exception message.
|
||||
* @param int $code The exception code.
|
||||
* @param E|null $previous Previous exception thrown,
|
||||
* or NULL if there is none.
|
||||
* @param int|string|resource|null $fragment The fragment up until the
|
||||
* point of failure.
|
||||
* On failure with sending, this is the number of bytes sent
|
||||
* successfully before the failure.
|
||||
* On failure when receiving, this is a string/stream holding
|
||||
* the contents received successfully before the failure.
|
||||
*
|
||||
* @return StreamException The exception to then be thrown.
|
||||
*/
|
||||
protected function createException(
|
||||
$message,
|
||||
$code = 0,
|
||||
E $previous = null,
|
||||
$fragment = null
|
||||
) {
|
||||
return new StreamException($message, $code, $previous, $fragment);
|
||||
}
|
||||
}
|
119
system/autoload/PEAR2/Net/Transmitter/StreamException.php
Normal file
119
system/autoload/PEAR2/Net/Transmitter/StreamException.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* Base for this exception.
|
||||
*/
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Used to enable any exception in chaining.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* Exception thrown when something goes wrong with the connection.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
class StreamException extends RuntimeException implements Exception
|
||||
{
|
||||
/**
|
||||
* @var int|string|resource|null The fragment up until the point of failure.
|
||||
* On failure with sending, this is the number of bytes sent
|
||||
* successfully before the failure.
|
||||
* On failure when receiving, this is a string/stream holding
|
||||
* the contents received successfully before the failure.
|
||||
* NULL if the failure occured before the operation started.
|
||||
*/
|
||||
protected $fragment = null;
|
||||
|
||||
/**
|
||||
* Creates a new stream exception.
|
||||
*
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param int $code The Exception code.
|
||||
* @param E|null $previous Previous exception thrown,
|
||||
* or NULL if there is none.
|
||||
* @param int|string|resource|null $fragment The fragment up until the
|
||||
* point of failure.
|
||||
* On failure with sending, this is the number of bytes sent
|
||||
* successfully before the failure.
|
||||
* On failure when receiving, this is a string/stream holding
|
||||
* the contents received successfully before the failure.
|
||||
* NULL if the failure occured before the operation started.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code,
|
||||
E $previous = null,
|
||||
$fragment = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->fragment = $fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stream fragment.
|
||||
*
|
||||
* @return int|string|resource|null The fragment up until the
|
||||
* point of failure.
|
||||
* On failure with sending, this is the number of bytes sent
|
||||
* successfully before the failure.
|
||||
* On failure when receiving, this is a string/stream holding
|
||||
* the contents received successfully before the failure.
|
||||
* NULL if the failure occured before the operation started.
|
||||
*/
|
||||
public function getFragment()
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// Unreliable in testing.
|
||||
|
||||
/**
|
||||
* Returns a string representation of the exception.
|
||||
*
|
||||
* @return string The exception as a string.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if (null !== $this->fragment) {
|
||||
$result .= "\nFragment: ";
|
||||
if (is_scalar($this->fragment)) {
|
||||
$result .= (string)$this->fragment;
|
||||
} else {
|
||||
$result .= stream_get_contents($this->fragment);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
400
system/autoload/PEAR2/Net/Transmitter/TcpClient.php
Normal file
400
system/autoload/PEAR2/Net/Transmitter/TcpClient.php
Normal file
@ -0,0 +1,400 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
/**
|
||||
* Used for managing persistent connections.
|
||||
*/
|
||||
use PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* Used for matching arbitrary exceptions in
|
||||
* {@link TcpClient::createException()} and releasing locks properly.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* A socket transmitter.
|
||||
*
|
||||
* This is a convinience wrapper for socket functionality. Used to ensure data
|
||||
* integrity.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
class TcpClient extends NetworkStream
|
||||
{
|
||||
|
||||
/**
|
||||
* @var int The error code of the last error on the socket.
|
||||
*/
|
||||
protected $errorNo = 0;
|
||||
|
||||
/**
|
||||
* @var string The error message of the last error on the socket.
|
||||
*/
|
||||
protected $errorStr = null;
|
||||
|
||||
/**
|
||||
* @var SHM Persistent connection handler. Remains NULL for non-persistent
|
||||
* connections.
|
||||
*/
|
||||
protected $shmHandler = null;
|
||||
|
||||
/**
|
||||
* @var array An array with all connections from this PHP request (as keys)
|
||||
* and their lock state (as a value).
|
||||
*/
|
||||
protected static $lockState = array();
|
||||
|
||||
protected static $cryptoScheme = array(
|
||||
parent::CRYPTO_OFF => 'tcp',
|
||||
parent::CRYPTO_SSL2 => 'sslv2',
|
||||
parent::CRYPTO_SSL3 => 'sslv3',
|
||||
parent::CRYPTO_SSL => 'ssl',
|
||||
parent::CRYPTO_TLS => 'tls'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string The URI of this connection.
|
||||
*/
|
||||
protected $uri;
|
||||
|
||||
/**
|
||||
* Creates a new connection with the specified options.
|
||||
*
|
||||
* @param string $host Hostname (IP or domain) of the server.
|
||||
* @param int $port The port on the server.
|
||||
* @param bool $persist Whether or not the connection should be a
|
||||
* persistent one.
|
||||
* @param float $timeout The timeout for the connection.
|
||||
* @param string $key A string that uniquely identifies the
|
||||
* connection.
|
||||
* @param string $crypto Encryption setting. Must be one of the
|
||||
* NetworkStream::CRYPTO_* constants. By default, encryption is
|
||||
* disabled. If the setting has an associated scheme for it, it will be
|
||||
* used, and if not, the setting will be adjusted right after the
|
||||
* connection is estabilished.
|
||||
* @param resource $context A context for the socket.
|
||||
*/
|
||||
public function __construct(
|
||||
$host,
|
||||
$port,
|
||||
$persist = false,
|
||||
$timeout = null,
|
||||
$key = '',
|
||||
$crypto = parent::CRYPTO_OFF,
|
||||
$context = null
|
||||
) {
|
||||
$this->streamType = '_CLIENT';
|
||||
|
||||
if (strpos($host, ':') !== false) {
|
||||
$host = "[{$host}]";
|
||||
}
|
||||
$flags = STREAM_CLIENT_CONNECT;
|
||||
if ($persist) {
|
||||
$flags |= STREAM_CLIENT_PERSISTENT;
|
||||
}
|
||||
|
||||
$timeout
|
||||
= null == $timeout ? ini_get('default_socket_timeout') : $timeout;
|
||||
|
||||
$key = rawurlencode($key);
|
||||
|
||||
if (null === $context) {
|
||||
$context = stream_context_get_default();
|
||||
} elseif ((!is_resource($context))
|
||||
|| ('stream-context' !== get_resource_type($context))
|
||||
) {
|
||||
throw $this->createException('Invalid context supplied.', 6);
|
||||
}
|
||||
$hasCryptoScheme = array_key_exists($crypto, static::$cryptoScheme);
|
||||
$scheme = $hasCryptoScheme ? static::$cryptoScheme[$crypto] : 'tcp';
|
||||
$this->uri = "{$scheme}://{$host}:{$port}/{$key}";
|
||||
set_error_handler(array($this, 'handleError'));
|
||||
try {
|
||||
parent::__construct(
|
||||
stream_socket_client(
|
||||
$this->uri,
|
||||
$this->errorNo,
|
||||
$this->errorStr,
|
||||
$timeout,
|
||||
$flags,
|
||||
$context
|
||||
)
|
||||
);
|
||||
restore_error_handler();
|
||||
} catch (E $e) {
|
||||
restore_error_handler();
|
||||
if (0 === $this->errorNo) {
|
||||
throw $this->createException(
|
||||
'Failed to initialize socket.',
|
||||
7,
|
||||
$e
|
||||
);
|
||||
}
|
||||
throw $this->createException(
|
||||
'Failed to connect with socket.',
|
||||
8,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
if ($hasCryptoScheme) {
|
||||
$this->crypto = $crypto;
|
||||
} elseif (parent::CRYPTO_OFF !== $crypto) {
|
||||
$this->setCrypto($crypto);
|
||||
}
|
||||
$this->setIsBlocking(parent::CRYPTO_OFF === $crypto);
|
||||
|
||||
if ($persist) {
|
||||
$this->shmHandler = SHM::factory(
|
||||
__CLASS__ . ' ' . $this->uri . ' '
|
||||
);
|
||||
self::$lockState[$this->uri] = self::DIRECTION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*
|
||||
* Creates a new exception. Used by the rest of the functions in this class.
|
||||
*
|
||||
* @param string $message The exception message.
|
||||
* @param int $code The exception code.
|
||||
* @param E|null $previous Previous exception thrown,
|
||||
* or NULL if there is none.
|
||||
* @param int|string|resource|null $fragment The fragment up until the
|
||||
* point of failure.
|
||||
* On failure with sending, this is the number of bytes sent
|
||||
* successfully before the failure.
|
||||
* On failure when receiving, this is a string/stream holding
|
||||
* the contents received successfully before the failure.
|
||||
*
|
||||
* @return SocketException The exception to then be thrown.
|
||||
*/
|
||||
protected function createException(
|
||||
$message,
|
||||
$code = 0,
|
||||
E $previous = null,
|
||||
$fragment = null
|
||||
) {
|
||||
return new SocketException(
|
||||
$message,
|
||||
$code,
|
||||
$previous,
|
||||
$fragment,
|
||||
$this->errorNo,
|
||||
$this->errorStr
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks transmission.
|
||||
*
|
||||
* Locks transmission in one or more directions. Useful when dealing with
|
||||
* persistent connections. Note that every send/receive call implicitly
|
||||
* calls this function and then restores it to the previous state. You only
|
||||
* need to call this function if you need to do an uninterrputed sequence of
|
||||
* such calls.
|
||||
*
|
||||
* @param int $direction The direction(s) to have locked. Acceptable values
|
||||
* are the DIRECTION_* constants. If a lock for a direction can't be
|
||||
* obtained immediatly, the function will block until one is aquired.
|
||||
* Note that if you specify {@link DIRECTION_ALL}, the sending lock will
|
||||
* be obtained before the receiving one, and if obtaining the receiving
|
||||
* lock afterwards fails, the sending lock will be released too.
|
||||
* @param bool $replace Whether to replace all locks with the specified
|
||||
* ones. Setting this to FALSE will make the function only obtain the
|
||||
* locks which are not already obtained.
|
||||
*
|
||||
* @return int|false The previous state or FALSE if the connection is not
|
||||
* persistent or arguments are invalid.
|
||||
*/
|
||||
public function lock($direction = self::DIRECTION_ALL, $replace = false)
|
||||
{
|
||||
if ($this->persist && is_int($direction)) {
|
||||
$old = self::$lockState[$this->uri];
|
||||
|
||||
if ($direction & self::DIRECTION_SEND) {
|
||||
if (($old & self::DIRECTION_SEND)
|
||||
|| $this->shmHandler->lock(self::DIRECTION_SEND)
|
||||
) {
|
||||
self::$lockState[$this->uri] |= self::DIRECTION_SEND;
|
||||
} else {
|
||||
throw new LockException('Unable to obtain sending lock.');
|
||||
}
|
||||
} elseif ($replace) {
|
||||
if (!($old & self::DIRECTION_SEND)
|
||||
|| $this->shmHandler->unlock(self::DIRECTION_SEND)
|
||||
) {
|
||||
self::$lockState[$this->uri] &= ~self::DIRECTION_SEND;
|
||||
} else {
|
||||
throw new LockException('Unable to release sending lock.');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ($direction & self::DIRECTION_RECEIVE) {
|
||||
if (($old & self::DIRECTION_RECEIVE)
|
||||
|| $this->shmHandler->lock(self::DIRECTION_RECEIVE)
|
||||
) {
|
||||
self::$lockState[$this->uri] |= self::DIRECTION_RECEIVE;
|
||||
} else {
|
||||
throw new LockException(
|
||||
'Unable to obtain receiving lock.'
|
||||
);
|
||||
}
|
||||
} elseif ($replace) {
|
||||
if (!($old & self::DIRECTION_RECEIVE)
|
||||
|| $this->shmHandler->unlock(self::DIRECTION_RECEIVE)
|
||||
) {
|
||||
self::$lockState[$this->uri]
|
||||
&= ~self::DIRECTION_RECEIVE;
|
||||
} else {
|
||||
throw new LockException(
|
||||
'Unable to release receiving lock.'
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (LockException $e) {
|
||||
if ($direction & self::DIRECTION_SEND
|
||||
&& !($old & self::DIRECTION_SEND)
|
||||
) {
|
||||
$this->shmHandler->unlock(self::DIRECTION_SEND);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
return $old;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a string or stream to the server.
|
||||
*
|
||||
* Sends a string or stream to the server. If a seekable stream is
|
||||
* provided, it will be seeked back to the same position it was passed as,
|
||||
* regardless of the $offset parameter.
|
||||
*
|
||||
* @param string|resource $contents The string or stream to send.
|
||||
* @param int $offset The offset from which to start sending.
|
||||
* If a stream is provided, and this is set to NULL, sending will start
|
||||
* from the current stream position.
|
||||
* @param int $length The maximum length to send. If omitted,
|
||||
* the string/stream will be sent to its end.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
*/
|
||||
public function send($contents, $offset = null, $length = null)
|
||||
{
|
||||
if (false === ($previousState = $this->lock(self::DIRECTION_SEND))
|
||||
&& $this->persist
|
||||
) {
|
||||
throw $this->createException(
|
||||
'Unable to obtain sending lock',
|
||||
10
|
||||
);
|
||||
}
|
||||
try {
|
||||
$result = parent::send($contents, $offset, $length);
|
||||
} catch (E $e) {
|
||||
$this->lock($previousState, true);
|
||||
throw $e;
|
||||
}
|
||||
$this->lock($previousState, true);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives data from the server.
|
||||
*
|
||||
* Receives data from the server as a string.
|
||||
*
|
||||
* @param int $length The number of bytes to receive.
|
||||
* @param string $what Descriptive string about what is being received
|
||||
* (used in exception messages).
|
||||
*
|
||||
* @return string The received content.
|
||||
*/
|
||||
public function receive($length, $what = 'data')
|
||||
{
|
||||
if (false === ($previousState = $this->lock(self::DIRECTION_RECEIVE))
|
||||
&& $this->persist
|
||||
) {
|
||||
throw $this->createException(
|
||||
'Unable to obtain receiving lock',
|
||||
9
|
||||
);
|
||||
}
|
||||
try {
|
||||
$result = parent::receive($length, $what);
|
||||
} catch (E $e) {
|
||||
$this->lock($previousState, true);
|
||||
throw $e;
|
||||
}
|
||||
$this->lock($previousState, true);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives data from the server.
|
||||
*
|
||||
* Receives data from the server as a stream.
|
||||
*
|
||||
* @param int $length The number of bytes to receive.
|
||||
* @param FilterCollection $filters A collection of filters to apply to the
|
||||
* stream while receiving. Note that the filters will not be present on
|
||||
* the stream after receiving is done.
|
||||
* @param string $what Descriptive string about what is being
|
||||
* received (used in exception messages).
|
||||
*
|
||||
* @return resource The received content.
|
||||
*/
|
||||
public function receiveStream(
|
||||
$length,
|
||||
FilterCollection $filters = null,
|
||||
$what = 'stream data'
|
||||
) {
|
||||
if (false === ($previousState = $this->lock(self::DIRECTION_RECEIVE))
|
||||
&& $this->persist
|
||||
) {
|
||||
throw $this->createException(
|
||||
'Unable to obtain receiving lock',
|
||||
9
|
||||
);
|
||||
}
|
||||
try {
|
||||
$result = parent::receiveStream($length, $filters, $what);
|
||||
} catch (E $e) {
|
||||
$this->lock($previousState, true);
|
||||
throw $e;
|
||||
}
|
||||
$this->lock($previousState, true);
|
||||
return $result;
|
||||
}
|
||||
}
|
147
system/autoload/PEAR2/Net/Transmitter/TcpServerConnection.php
Normal file
147
system/autoload/PEAR2/Net/Transmitter/TcpServerConnection.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for network stream functionality.
|
||||
*
|
||||
* PHP has built in support for various types of network streams, such as HTTP and TCP sockets. One problem that arises with them is the fact that a single fread/fwrite call might not read/write all the data you intended, regardless of whether you're in blocking mode or not. While the PHP manual offers a workaround in the form of a loop with a few variables, using it every single time you want to read/write can be tedious.
|
||||
|
||||
This package abstracts this away, so that when you want to get exactly N amount of bytes, you can be sure the upper levels of your app will be dealing with N bytes. Oh, and the functionality is nicely wrapped in an object (but that's just the icing on the cake).
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0a5
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\Transmitter;
|
||||
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* A transmitter for connections to a socket server.
|
||||
*
|
||||
* This is a convinience wrapper for functionality of socket server connections.
|
||||
* Used to ensure data integrity. Server handling is not part of the class in
|
||||
* order to allow its usage as part of various server implementations (e.g. fork
|
||||
* and/or sequential).
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_Transmitter
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_Transmitter
|
||||
*/
|
||||
class TcpServerConnection extends NetworkStream
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string The IP address of the connected client.
|
||||
*/
|
||||
protected $peerIP;
|
||||
|
||||
/**
|
||||
* @var int The port of the connected client.
|
||||
*/
|
||||
protected $peerPort;
|
||||
|
||||
/**
|
||||
* Creates a new connection with the specified options.
|
||||
*
|
||||
* @param resource $server A socket server, created with
|
||||
* {@link stream_socket_server()}.
|
||||
* @param float $timeout The timeout for the connection.
|
||||
*/
|
||||
public function __construct($server, $timeout = null)
|
||||
{
|
||||
$this->streamType = '_SERVER';
|
||||
|
||||
if (!self::isStream($server)) {
|
||||
throw $this->createException('Invalid server supplied.', 9);
|
||||
}
|
||||
$timeout
|
||||
= null == $timeout ? ini_get('default_socket_timeout') : $timeout;
|
||||
|
||||
set_error_handler(array($this, 'handleError'));
|
||||
try {
|
||||
parent::__construct(
|
||||
stream_socket_accept($server, $timeout, $peername)
|
||||
);
|
||||
restore_error_handler();
|
||||
$portString = strrchr($peername, ':');
|
||||
$this->peerPort = (int) substr($portString, 1);
|
||||
$ipString = substr(
|
||||
$peername,
|
||||
0,
|
||||
strlen($peername) - strlen($portString)
|
||||
);
|
||||
if (strpos($ipString, '[') === 0
|
||||
&& strpos(strrev($ipString), ']') === 0
|
||||
) {
|
||||
$ipString = substr($ipString, 1, strlen($ipString) - 2);
|
||||
}
|
||||
$this->peerIP = $ipString;
|
||||
} catch (E $e) {
|
||||
restore_error_handler();
|
||||
throw $this->createException(
|
||||
'Failed to initialize connection.',
|
||||
10,
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the IP address of the connected client.
|
||||
*
|
||||
* @return string The IP address of the connected client.
|
||||
*/
|
||||
public function getPeerIP()
|
||||
{
|
||||
return $this->peerIP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port of the connected client.
|
||||
*
|
||||
* @return int The port of the connected client.
|
||||
*/
|
||||
public function getPeerPort()
|
||||
{
|
||||
return $this->peerPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*
|
||||
* Creates a new exception. Used by the rest of the functions in this class.
|
||||
*
|
||||
* @param string $message The exception message.
|
||||
* @param int $code The exception code.
|
||||
* @param E|null $previous Previous exception thrown, or NULL if there
|
||||
* is none.
|
||||
* @param string|null $fragment The fragment up until the point of failure.
|
||||
* NULL if the failure occured before the operation started.
|
||||
*
|
||||
* @return SocketException The exception to then be thrown.
|
||||
*/
|
||||
protected function createException(
|
||||
$message,
|
||||
$code = 0,
|
||||
E $previous = null,
|
||||
$fragment = null
|
||||
) {
|
||||
return new SocketException(
|
||||
$message,
|
||||
$code,
|
||||
$previous,
|
||||
$fragment
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user