git-svn-id: file:///home/shish/svn/shimmie2/trunk@1068 7f39781d-f577-437e-ae19-be835c7a54ca
		
			
				
	
	
		
			624 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  *  base include file for SimpleTest
 | |
|  *  @package    SimpleTest
 | |
|  *  @subpackage WebTester
 | |
|  *  @version    $Id: http.php 1722 2008-04-07 19:30:56Z lastcraft $
 | |
|  */
 | |
| 
 | |
| /**#@+
 | |
|  *  include other SimpleTest class files
 | |
|  */
 | |
| require_once(dirname(__FILE__) . '/socket.php');
 | |
| require_once(dirname(__FILE__) . '/cookies.php');
 | |
| require_once(dirname(__FILE__) . '/url.php');
 | |
| /**#@-*/
 | |
| 
 | |
| /**
 | |
|  *    Creates HTTP headers for the end point of
 | |
|  *    a HTTP request.
 | |
|  *    @package SimpleTest
 | |
|  *    @subpackage WebTester
 | |
|  */
 | |
| class SimpleRoute {
 | |
|     var $_url;
 | |
|     
 | |
|     /**
 | |
|      *    Sets the target URL.
 | |
|      *    @param SimpleUrl $url   URL as object.
 | |
|      *    @access public
 | |
|      */
 | |
|     function SimpleRoute($url) {
 | |
|         $this->_url = $url;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Resource name.
 | |
|      *    @return SimpleUrl        Current url.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function getUrl() {
 | |
|         return $this->_url;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Creates the first line which is the actual request.
 | |
|      *    @param string $method   HTTP request method, usually GET.
 | |
|      *    @return string          Request line content.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function _getRequestLine($method) {
 | |
|         return $method . ' ' . $this->_url->getPath() .
 | |
|                 $this->_url->getEncodedRequest() . ' HTTP/1.0';
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Creates the host part of the request.
 | |
|      *    @return string          Host line content.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function _getHostLine() {
 | |
|         $line = 'Host: ' . $this->_url->getHost();
 | |
|         if ($this->_url->getPort()) {
 | |
|             $line .= ':' . $this->_url->getPort();
 | |
|         }
 | |
|         return $line;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Opens a socket to the route.
 | |
|      *    @param string $method      HTTP request method, usually GET.
 | |
|      *    @param integer $timeout    Connection timeout.
 | |
|      *    @return SimpleSocket       New socket.
 | |
|      *    @access public
 | |
|      */
 | |
|     function &createConnection($method, $timeout) {
 | |
|         $default_port = ('https' == $this->_url->getScheme()) ? 443 : 80;
 | |
|         $socket = &$this->_createSocket(
 | |
|                 $this->_url->getScheme() ? $this->_url->getScheme() : 'http',
 | |
|                 $this->_url->getHost(),
 | |
|                 $this->_url->getPort() ? $this->_url->getPort() : $default_port,
 | |
|                 $timeout);
 | |
|         if (! $socket->isError()) {
 | |
|             $socket->write($this->_getRequestLine($method) . "\r\n");
 | |
|             $socket->write($this->_getHostLine() . "\r\n");
 | |
|             $socket->write("Connection: close\r\n");
 | |
|         }
 | |
|         return $socket;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Factory for socket.
 | |
|      *    @param string $scheme                   Protocol to use.
 | |
|      *    @param string $host                     Hostname to connect to.
 | |
|      *    @param integer $port                    Remote port.
 | |
|      *    @param integer $timeout                 Connection timeout.
 | |
|      *    @return SimpleSocket/SimpleSecureSocket New socket.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function &_createSocket($scheme, $host, $port, $timeout) {
 | |
|         if (in_array($scheme, array('https'))) {
 | |
|             $socket = &new SimpleSecureSocket($host, $port, $timeout);
 | |
|         } else {
 | |
|             $socket = &new SimpleSocket($host, $port, $timeout);
 | |
|         }
 | |
|         return $socket;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  *    Creates HTTP headers for the end point of
 | |
|  *    a HTTP request via a proxy server.
 | |
|  *    @package SimpleTest
 | |
|  *    @subpackage WebTester
 | |
|  */
 | |
| class SimpleProxyRoute extends SimpleRoute {
 | |
|     var $_proxy;
 | |
|     var $_username;
 | |
|     var $_password;
 | |
|     
 | |
|     /**
 | |
|      *    Stashes the proxy address.
 | |
|      *    @param SimpleUrl $url     URL as object.
 | |
|      *    @param string $proxy      Proxy URL.
 | |
|      *    @param string $username   Username for autentication.
 | |
|      *    @param string $password   Password for autentication.
 | |
|      *    @access public
 | |
|      */
 | |
|     function SimpleProxyRoute($url, $proxy, $username = false, $password = false) {
 | |
|         $this->SimpleRoute($url);
 | |
|         $this->_proxy = $proxy;
 | |
|         $this->_username = $username;
 | |
|         $this->_password = $password;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Creates the first line which is the actual request.
 | |
|      *    @param string $method   HTTP request method, usually GET.
 | |
|      *    @param SimpleUrl $url   URL as object.
 | |
|      *    @return string          Request line content.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function _getRequestLine($method) {
 | |
|         $url = $this->getUrl();
 | |
|         $scheme = $url->getScheme() ? $url->getScheme() : 'http';
 | |
|         $port = $url->getPort() ? ':' . $url->getPort() : '';
 | |
|         return $method . ' ' . $scheme . '://' . $url->getHost() . $port .
 | |
|                 $url->getPath() . $url->getEncodedRequest() . ' HTTP/1.0';
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Creates the host part of the request.
 | |
|      *    @param SimpleUrl $url   URL as object.
 | |
|      *    @return string          Host line content.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function _getHostLine() {
 | |
|         $host = 'Host: ' . $this->_proxy->getHost();
 | |
|         $port = $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080;
 | |
|         return "$host:$port";
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Opens a socket to the route.
 | |
|      *    @param string $method       HTTP request method, usually GET.
 | |
|      *    @param integer $timeout     Connection timeout.
 | |
|      *    @return SimpleSocket        New socket.
 | |
|      *    @access public
 | |
|      */
 | |
|     function &createConnection($method, $timeout) {
 | |
|         $socket = &$this->_createSocket(
 | |
|                 $this->_proxy->getScheme() ? $this->_proxy->getScheme() : 'http',
 | |
|                 $this->_proxy->getHost(),
 | |
|                 $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080,
 | |
|                 $timeout);
 | |
|         if ($socket->isError()) {
 | |
|             return $socket;
 | |
|         }
 | |
|         $socket->write($this->_getRequestLine($method) . "\r\n");
 | |
|         $socket->write($this->_getHostLine() . "\r\n");
 | |
|         if ($this->_username && $this->_password) {
 | |
|             $socket->write('Proxy-Authorization: Basic ' .
 | |
|                     base64_encode($this->_username . ':' . $this->_password) .
 | |
|                     "\r\n");
 | |
|         }
 | |
|         $socket->write("Connection: close\r\n");
 | |
|         return $socket;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  *    HTTP request for a web page. Factory for
 | |
|  *    HttpResponse object.
 | |
|  *    @package SimpleTest
 | |
|  *    @subpackage WebTester
 | |
|  */
 | |
| class SimpleHttpRequest {
 | |
|     var $_route;
 | |
|     var $_encoding;
 | |
|     var $_headers;
 | |
|     var $_cookies;
 | |
|     
 | |
|     /**
 | |
|      *    Builds the socket request from the different pieces.
 | |
|      *    These include proxy information, URL, cookies, headers,
 | |
|      *    request method and choice of encoding.
 | |
|      *    @param SimpleRoute $route              Request route.
 | |
|      *    @param SimpleFormEncoding $encoding    Content to send with
 | |
|      *                                           request.
 | |
|      *    @access public
 | |
|      */
 | |
|     function SimpleHttpRequest(&$route, $encoding) {
 | |
|         $this->_route = &$route;
 | |
|         $this->_encoding = $encoding;
 | |
|         $this->_headers = array();
 | |
|         $this->_cookies = array();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Dispatches the content to the route's socket.
 | |
|      *    @param integer $timeout      Connection timeout.
 | |
|      *    @return SimpleHttpResponse   A response which may only have
 | |
|      *                                 an error, but hopefully has a
 | |
|      *                                 complete web page.
 | |
|      *    @access public
 | |
|      */
 | |
|     function &fetch($timeout) {
 | |
|         $socket = &$this->_route->createConnection($this->_encoding->getMethod(), $timeout);
 | |
|         if (! $socket->isError()) {
 | |
|             $this->_dispatchRequest($socket, $this->_encoding);
 | |
|         }
 | |
|         $response = &$this->_createResponse($socket);
 | |
|         return $response;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Sends the headers.
 | |
|      *    @param SimpleSocket $socket           Open socket.
 | |
|      *    @param string $method                 HTTP request method,
 | |
|      *                                          usually GET.
 | |
|      *    @param SimpleFormEncoding $encoding   Content to send with request.
 | |
|      *    @access private
 | |
|      */
 | |
|     function _dispatchRequest(&$socket, $encoding) {
 | |
|         foreach ($this->_headers as $header_line) {
 | |
|             $socket->write($header_line . "\r\n");
 | |
|         }
 | |
|         if (count($this->_cookies) > 0) {
 | |
|             $socket->write("Cookie: " . implode(";", $this->_cookies) . "\r\n");
 | |
|         }
 | |
|         $encoding->writeHeadersTo($socket);
 | |
|         $socket->write("\r\n");
 | |
|         $encoding->writeTo($socket);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Adds a header line to the request.
 | |
|      *    @param string $header_line    Text of full header line.
 | |
|      *    @access public
 | |
|      */
 | |
|     function addHeaderLine($header_line) {
 | |
|         $this->_headers[] = $header_line;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Reads all the relevant cookies from the
 | |
|      *    cookie jar.
 | |
|      *    @param SimpleCookieJar $jar     Jar to read
 | |
|      *    @param SimpleUrl $url           Url to use for scope.
 | |
|      *    @access public
 | |
|      */
 | |
|     function readCookiesFromJar($jar, $url) {
 | |
|         $this->_cookies = $jar->selectAsPairs($url);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Wraps the socket in a response parser.
 | |
|      *    @param SimpleSocket $socket   Responding socket.
 | |
|      *    @return SimpleHttpResponse    Parsed response object.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function &_createResponse(&$socket) {
 | |
|         $response = &new SimpleHttpResponse(
 | |
|                 $socket,
 | |
|                 $this->_route->getUrl(),
 | |
|                 $this->_encoding);
 | |
|         return $response;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  *    Collection of header lines in the response.
 | |
|  *    @package SimpleTest
 | |
|  *    @subpackage WebTester
 | |
|  */
 | |
| class SimpleHttpHeaders {
 | |
|     var $_raw_headers;
 | |
|     var $_response_code;
 | |
|     var $_http_version;
 | |
|     var $_mime_type;
 | |
|     var $_location;
 | |
|     var $_cookies;
 | |
|     var $_authentication;
 | |
|     var $_realm;
 | |
|     
 | |
|     /**
 | |
|      *    Parses the incoming header block.
 | |
|      *    @param string $headers     Header block.
 | |
|      *    @access public
 | |
|      */
 | |
|     function SimpleHttpHeaders($headers) {
 | |
|         $this->_raw_headers = $headers;
 | |
|         $this->_response_code = false;
 | |
|         $this->_http_version = false;
 | |
|         $this->_mime_type = '';
 | |
|         $this->_location = false;
 | |
|         $this->_cookies = array();
 | |
|         $this->_authentication = false;
 | |
|         $this->_realm = false;
 | |
|         foreach (split("\r\n", $headers) as $header_line) {
 | |
|             $this->_parseHeaderLine($header_line);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for parsed HTTP protocol version.
 | |
|      *    @return integer           HTTP error code.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getHttpVersion() {
 | |
|         return $this->_http_version;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for raw header block.
 | |
|      *    @return string        All headers as raw string.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getRaw() {
 | |
|         return $this->_raw_headers;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for parsed HTTP error code.
 | |
|      *    @return integer           HTTP error code.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getResponseCode() {
 | |
|         return (integer)$this->_response_code;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Returns the redirected URL or false if
 | |
|      *    no redirection.
 | |
|      *    @return string      URL or false for none.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getLocation() {
 | |
|         return $this->_location;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Test to see if the response is a valid redirect.
 | |
|      *    @return boolean       True if valid redirect.
 | |
|      *    @access public
 | |
|      */
 | |
|     function isRedirect() {
 | |
|         return in_array($this->_response_code, array(301, 302, 303, 307)) &&
 | |
|                 (boolean)$this->getLocation();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Test to see if the response is an authentication
 | |
|      *    challenge.
 | |
|      *    @return boolean       True if challenge.
 | |
|      *    @access public
 | |
|      */
 | |
|     function isChallenge() {
 | |
|         return ($this->_response_code == 401) &&
 | |
|                 (boolean)$this->_authentication &&
 | |
|                 (boolean)$this->_realm;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for MIME type header information.
 | |
|      *    @return string           MIME type.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getMimeType() {
 | |
|         return $this->_mime_type;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for authentication type.
 | |
|      *    @return string        Type.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getAuthentication() {
 | |
|         return $this->_authentication;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for security realm.
 | |
|      *    @return string        Realm.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getRealm() {
 | |
|         return $this->_realm;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Writes new cookies to the cookie jar.
 | |
|      *    @param SimpleCookieJar $jar   Jar to write to.
 | |
|      *    @param SimpleUrl $url         Host and path to write under.
 | |
|      *    @access public
 | |
|      */
 | |
|     function writeCookiesToJar(&$jar, $url) {
 | |
|         foreach ($this->_cookies as $cookie) {
 | |
|             $jar->setCookie(
 | |
|                     $cookie->getName(),
 | |
|                     $cookie->getValue(),
 | |
|                     $url->getHost(),
 | |
|                     $cookie->getPath(),
 | |
|                     $cookie->getExpiry());
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      *    Called on each header line to accumulate the held
 | |
|      *    data within the class.
 | |
|      *    @param string $header_line        One line of header.
 | |
|      *    @access protected
 | |
|      */
 | |
|     function _parseHeaderLine($header_line) {
 | |
|         if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i', $header_line, $matches)) {
 | |
|             $this->_http_version = $matches[1];
 | |
|             $this->_response_code = $matches[2];
 | |
|         }
 | |
|         if (preg_match('/Content-type:\s*(.*)/i', $header_line, $matches)) {
 | |
|             $this->_mime_type = trim($matches[1]);
 | |
|         }
 | |
|         if (preg_match('/Location:\s*(.*)/i', $header_line, $matches)) {
 | |
|             $this->_location = trim($matches[1]);
 | |
|         }
 | |
|         if (preg_match('/Set-cookie:(.*)/i', $header_line, $matches)) {
 | |
|             $this->_cookies[] = $this->_parseCookie($matches[1]);
 | |
|         }
 | |
|         if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i', $header_line, $matches)) {
 | |
|             $this->_authentication = $matches[1];
 | |
|             $this->_realm = trim($matches[2]);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Parse the Set-cookie content.
 | |
|      *    @param string $cookie_line    Text after "Set-cookie:"
 | |
|      *    @return SimpleCookie          New cookie object.
 | |
|      *    @access private
 | |
|      */
 | |
|     function _parseCookie($cookie_line) {
 | |
|         $parts = split(";", $cookie_line);
 | |
|         $cookie = array();
 | |
|         preg_match('/\s*(.*?)\s*=(.*)/', array_shift($parts), $cookie);
 | |
|         foreach ($parts as $part) {
 | |
|             if (preg_match('/\s*(.*?)\s*=(.*)/', $part, $matches)) {
 | |
|                 $cookie[$matches[1]] = trim($matches[2]);
 | |
|             }
 | |
|         }
 | |
|         return new SimpleCookie(
 | |
|                 $cookie[1],
 | |
|                 trim($cookie[2]),
 | |
|                 isset($cookie["path"]) ? $cookie["path"] : "",
 | |
|                 isset($cookie["expires"]) ? $cookie["expires"] : false);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  *    Basic HTTP response.
 | |
|  *    @package SimpleTest
 | |
|  *    @subpackage WebTester
 | |
|  */
 | |
| class SimpleHttpResponse extends SimpleStickyError {
 | |
|     var $_url;
 | |
|     var $_encoding;
 | |
|     var $_sent;
 | |
|     var $_content;
 | |
|     var $_headers;
 | |
|     
 | |
|     /**
 | |
|      *    Constructor. Reads and parses the incoming
 | |
|      *    content and headers.
 | |
|      *    @param SimpleSocket $socket   Network connection to fetch
 | |
|      *                                  response text from.
 | |
|      *    @param SimpleUrl $url         Resource name.
 | |
|      *    @param mixed $encoding        Record of content sent.
 | |
|      *    @access public
 | |
|      */
 | |
|     function SimpleHttpResponse(&$socket, $url, $encoding) {
 | |
|         $this->SimpleStickyError();
 | |
|         $this->_url = $url;
 | |
|         $this->_encoding = $encoding;
 | |
|         $this->_sent = $socket->getSent();
 | |
|         $this->_content = false;
 | |
|         $raw = $this->_readAll($socket);
 | |
|         if ($socket->isError()) {
 | |
|             $this->_setError('Error reading socket [' . $socket->getError() . ']');
 | |
|             return;
 | |
|         }
 | |
|         $this->_parse($raw);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Splits up the headers and the rest of the content.
 | |
|      *    @param string $raw    Content to parse.
 | |
|      *    @access private
 | |
|      */
 | |
|     function _parse($raw) {
 | |
|         if (! $raw) {
 | |
|             $this->_setError('Nothing fetched');
 | |
|             $this->_headers = &new SimpleHttpHeaders('');
 | |
|         } elseif (! strstr($raw, "\r\n\r\n")) {
 | |
|             $this->_setError('Could not split headers from content');
 | |
|             $this->_headers = &new SimpleHttpHeaders($raw);
 | |
|         } else {
 | |
|             list($headers, $this->_content) = split("\r\n\r\n", $raw, 2);
 | |
|             $this->_headers = &new SimpleHttpHeaders($headers);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Original request method.
 | |
|      *    @return string        GET, POST or HEAD.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getMethod() {
 | |
|         return $this->_encoding->getMethod();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Resource name.
 | |
|      *    @return SimpleUrl        Current url.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getUrl() {
 | |
|         return $this->_url;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Original request data.
 | |
|      *    @return mixed              Sent content.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getRequestData() {
 | |
|         return $this->_encoding;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Raw request that was sent down the wire.
 | |
|      *    @return string        Bytes actually sent.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getSent() {
 | |
|         return $this->_sent;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for the content after the last
 | |
|      *    header line.
 | |
|      *    @return string           All content.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getContent() {
 | |
|         return $this->_content;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for header block. The response is the
 | |
|      *    combination of this and the content.
 | |
|      *    @return SimpleHeaders        Wrapped header block.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getHeaders() {
 | |
|         return $this->_headers;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Accessor for any new cookies.
 | |
|      *    @return array       List of new cookies.
 | |
|      *    @access public
 | |
|      */
 | |
|     function getNewCookies() {
 | |
|         return $this->_headers->getNewCookies();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Reads the whole of the socket output into a
 | |
|      *    single string.
 | |
|      *    @param SimpleSocket $socket  Unread socket.
 | |
|      *    @return string               Raw output if successful
 | |
|      *                                 else false.
 | |
|      *    @access private
 | |
|      */
 | |
|     function _readAll(&$socket) {
 | |
|         $all = '';
 | |
|         while (! $this->_isLastPacket($next = $socket->read())) {
 | |
|             $all .= $next;
 | |
|         }
 | |
|         return $all;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      *    Test to see if the packet from the socket is the
 | |
|      *    last one.
 | |
|      *    @param string $packet    Chunk to interpret.
 | |
|      *    @return boolean          True if empty or EOF.
 | |
|      *    @access private
 | |
|      */
 | |
|     function _isLastPacket($packet) {
 | |
|         if (is_string($packet)) {
 | |
|             return $packet === '';
 | |
|         }
 | |
|         return ! $packet;
 | |
|     }
 | |
| }
 | |
| ?>
 |