I was recently asked to release OneLobby's source code. It's an unfinished product but I think it would be a good idea. However, before I do that I'm going to showcase some of the useful classes I made for it. Four of these classes happen to be interfaces for dealing with PHP's socket and cURL functions. Take a look...
<?php
/*
OneLobby
Copyright (C) 2007 Peter Goodman, Geoffrey Goodman
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------
// The user Agent and version of this OneLobby remote
// XML caller.
//-------------------------------------------------
define ('REMOTE_XML_USERAGENT', 'OneLobby [RemoteXML v.1.0]');
//-------------------------------------------------
// Out little extension class.. it's not really needed,
// but it's convenient to choose REST or RPC.
//-------------------------------------------------
class RemoteXML extends FAExtension
{
function RemoteXML()
{
// moo :P
}
function &getFinder($type = 'REST')
{
$class = $type .'_Request';
$ret = NULL;
if(class_exists($class))
{
$ret = &new $class();
}
return $ret;
}
}
//-------------------------------------------------
// Base wrapper class for REST and RPC calls. Deal with
// opening connections using sockets or cURL, sending
// and receiving data, etc.
//-------------------------------------------------
class RemoteXMLRequest
{
//-------------------------------------------------
// Some variables related to RPC and REST requests.
//-------------------------------------------------
var $_initial_request = FALSE;
var $_initial_put = FALSE;
//-------------------------------------------------
// Get a cURL or Socket connection.
//-------------------------------------------------
function &getConnection()
{
//-------------------------------------------------
// If the cURL extension isn't loaded, try to load
// it. The PHP_SHLIB_SUFFIX is for checking if we are
// on Windows or UNIX.
//-------------------------------------------------
if(!extension_loaded('curl'))
{
$prefix = (PHP_SHLIB_SUFFIX === 'dll') ? 'php_' : '';
@dl($prefix . 'curl.'. PHP_SHLIB_SUFFIX);
}
if(function_exists('curl_init'))
{
$conn = &new cURL_Connection;
}
else
{
$conn = &new Socket_Connection;
}
return $conn;
}
//-------------------------------------------------
// Interface methods...
//-------------------------------------------------
function getInfo($url)
{
assert(FALSE);
}
function putInfo($url, $xml)
{
assert(FALSE);
}
}
//-------------------------------------------------
// Deal with REST (representational state transfers)
// requests.
//-------------------------------------------------
class REST_Request extends RemoteXMLRequest
{
//-------------------------------------------------
// Do the initial request of data for this REST session.
//-------------------------------------------------
function getInfo($url)
{
//-------------------------------------------------
// Get out socket/cURL connection and make the request.
//-------------------------------------------------
$conn = &$this->getConnection();
$conn->createRequest('GET', $url);
$data = $conn->getReturnData();
$conn->closeRequest();
$this->_initial_request = TRUE;
return $data;
}
//-------------------------------------------------
// Put info to the server.
//-------------------------------------------------
function putInfo($url, $xml)
{
//-------------------------------------------------
// Make sure we're doing things the REST way.
//-------------------------------------------------
if(!$this->_initial_request)
{
trigger_error("[ERROR] This is a REST application, not RPC.");
}
//-------------------------------------------------
// Use a socket connection for this for the sake of
// simplicity.
//-------------------------------------------------
$conn = &new Socket_Connection;
$conn->createRequest('PUT', $url);
$conn->setPutData($xml);
$data = $conn->getReturnData();
$conn->closeRequest();
return $data;
}
}
//-------------------------------------------------
// Deal with RPC (remote procedure call)
// requests.
//-------------------------------------------------
class RPC_Request extends RemoteXMLRequest
{
//-------------------------------------------------
// Put info to the server.
//-------------------------------------------------
function putInfo($url, $xml)
{
//-------------------------------------------------
// Use a socket connection for this for the sake of
// simplicity.
//-------------------------------------------------
$conn = &new Socket_Connection;
$conn->createRequest('PUT', $url);
$conn->setPutData($xml);
$data = $conn->getReturnData();
$conn->closeRequest();
$this->_initial_put = TRUE;
return $data;
}
//-------------------------------------------------
// Get the information after out inital PUT
//-------------------------------------------------
function getInfo($url)
{
//-------------------------------------------------
// Make sure we're doing things the RPC way.
//-------------------------------------------------
if(!$this->_initial_put)
{
trigger_error("[ERROR] This is a REST application, not RPC.", E_USER_ERROR);
}
//-------------------------------------------------
// Get out socket/cURL connection and make the request.
//-------------------------------------------------
$conn = &$this->getConnection();
$conn->createRequest('GET', $url);
$data = $conn->getReturnData();
$conn->closeRequest();
return $data;
}
}
//-------------------------------------------------
// Interface for cURL and socket connections.
//-------------------------------------------------
class RemoteXMLConnection
{
var $_conn;
var $_conn_closed = FALSE;
//-------------------------------------------------
// Some nice variables
//-------------------------------------------------
var $_scheme;
var $_server;
var $_path;
var $_port = 80;
var $_timeout = 5;
var $_put = FALSE;
var $_head = FALSE;
function createRequest($type = 'GET', $server = '', $path = '/', $port = 80)
{
assert(FALSE);
}
function closeRequest()
{
assert(FALSE);
}
function getReturnData()
{
assert(FALSE);
}
function setFile($file_name)
{
assert(FALSE);
}
function errorNo()
{
assert(FALSE);
}
function showAnyErrors()
{
assert(FALSE);
}
function setXMLData(&$data)
{
if(!is_a($data, 'RemoteXMLDocument'))
{
trigger_error("[ERROR] The data must be an instance of the RemoteXMLDocument object.");
}
}
function _parseUrl($url)
{
//-------------------------------------------------
// Parse the passed in URL.
//-------------------------------------------------
$url = parse_url($url);
$this->_scheme = strtoupper($url['scheme']);
$this->_server = $url['host'];
$this->_path = isset($url['path']) ? $url['path'] : '/';
$this->_port = isset($url['port']) ? $url['port'] : 80;
$this->_timeout = isset($url['fragment']) ? $url['fragment'] : 5;
}
}
//-------------------------------------------------
// A cURL layer for doing what we need to do :D
//-------------------------------------------------
class cURL_Connection extends RemoteXMLConnection
{
var $_called_exec = FALSE;
//-------------------------------------------------
// Create a request.
//-------------------------------------------------
function createRequest($method = 'GET', $url)
{
//-------------------------------------------------
// Parse the url to get the connection data.
//-------------------------------------------------
$this->_parseUrl($url);
//-------------------------------------------------
// Check to see if the cURL extension is even enabled.
//-------------------------------------------------
if(function_exists('curl_init'))
{
//-------------------------------------------------
// Start connecting and do stuff.
//-------------------------------------------------
$this->_conn = &curl_init();
$this->showAnyErrors();
curl_setopt($this->_conn, CURLOPT_URL, $this->_server . $this->_path);
curl_setopt($this->_conn, CURLOPT_HTTP_VERSION, 1.0);
curl_setopt($this->_conn, CURLOPT_USERAGENT, REMOTE_XML_USERAGENT);
$this->showAnyErrors();
//-------------------------------------------------
// Figure out what connection method we are using.
//-------------------------------------------------
$return_headers = FALSE;
if($method == 'GET')
{
$method = CURLOPT_HTTPGET;
}
else if($method == 'POST')
{
$method = CURLOPT_POST;
}
else if($method == 'HEAD')
{
$method = 'GET';
$return_headers = TRUE;
}
else
{
$method = CURLOPT_PUT;
$this->_put = TRUE;
}
curl_setopt($this->_conn, $method, 1);
//-------------------------------------------------
// Should we display the headers?
//-------------------------------------------------
curl_setopt($this->_conn, CURLOPT_HEADER, $return_headers);
//-------------------------------------------------
// If the request fails (error code > 400), fail silently.
//-------------------------------------------------
curl_setopt($this->_conn, CURLOPT_FAILONERROR, 1);
//-------------------------------------------------
// Follow header(Location: )'s, but only a max of 5
// of them.
//-------------------------------------------------
curl_setopt($this->_conn, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($this->_conn, CURLOPT_MAXREDIRS, 5);
//-------------------------------------------------
// Make sure to close the request.
//-------------------------------------------------
register_shutdown_function(array(&$this, 'closeRequest'));
}
else
{
trigger_error("[ERROR] The cURL extension cannot be found.");
}
}
//-------------------------------------------------
// Get the return data from the execution.
//-------------------------------------------------
function getReturnData()
{
curl_setopt($this->_conn, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($this->_conn);
$this->_called_exec = TRUE;
return $data;
}
//-------------------------------------------------
// Close the connection
//-------------------------------------------------
function closeRequest()
{
if(!$this->_conn_closed)
{
if(!$this->_called_exec)
{
curl_exec($this->_conn);
}
curl_close($this->_conn);
}
}
//-------------------------------------------------
// If we're using HTTP PUT, we need to 'put' a file to
// their server, so, open up a file pointer to the local
// file that we want to put and send it to their server.
//-------------------------------------------------
function setFile($file_name)
{
if($this->_put)
{
//-------------------------------------------------
// Do some nice error checking..
//-------------------------------------------------
if(!file_exists($file_name))
{
trigger_error("[ERROR] File [$file_name] does not exist.");
}
if(!is_readable($file_name))
{
trigger_error("[ERROR] File [$file_name] cannot be read.");
}
//-------------------------------------------------
// Get and put the file.
//-------------------------------------------------
$file_fp = fopen($file_name, "r");
$file_size = filesize($file_name);
curl_setopt($this->_conn, CURLOPT_INFILE, $file_fp);
curl_setopt($this->_conn, CURLOPT_INFILESIZE, $file_size);
$this->showAnyErrors();
}
}
//--------------------------------------------
// Send XML data through the curl session.
//--------------------------------------------
function setPutData($xml)
{
if($this->_put)
{
// TODO:{[Should I make this create a file only to pass it to cURL only to delete it after?[setPutData[cURL_Connection}
}
}
//-------------------------------------------------
// Return the last error code of the curl operation.
//-------------------------------------------------
function errorNo()
{
return curl_errno($this->_conn);
}
//-------------------------------------------------
// If there are any errors, show them.
//-------------------------------------------------
function showAnyErrors()
{
if($this->errorNo() > 0)
{
trigger_error(curl_error($this->_conn), E_USER_ERROR);
}
}
}
//-------------------------------------------------
// Do everything that the above cURL stuff does, but
// manually with the socket.
//-------------------------------------------------
class Socket_Connection extends RemoteXMLConnection
{
//-------------------------------------------------
// Open a socket to the url on the specified port.
//-------------------------------------------------
function createRequest($method = 'GET', $url)
{
//-------------------------------------------------
// Parse the url to get the connection data.
//-------------------------------------------------
$this->_parseUrl();
//-------------------------------------------------
// Connect...
//-------------------------------------------------
$scheme = $this->_scheme == 'HTTPS' ? 'ssl' : $this->_scheme;
$this->_conn = fsockopen($scheme .'://'. $this->_server, $this->_port, $error_number, $error_string, $this->_timeout);
//-------------------------------------------------
// Error check.
//-------------------------------------------------
if(!$this->_conn)
{
trigger_error("[ERROR] Could not open the socket.", E_USER_ERROR);
}
$data = stream_get_meta_data($this->_conn);
if($data['timed_out'])
{
trigger_error("[ERROR] Socket times out.", E_USER_ERROR);
}
//-------------------------------------------------
// Make sure it times out.
//-------------------------------------------------
stream_set_timeout($this->_conn, $this->_timeout);
stream_set_blocking($this->_conn, FALSE);
//-------------------------------------------------
// Figure out what connection method we are using.
//-------------------------------------------------
if($method == 'PUT')
{
$this->_put = TRUE;
}
else if($method == 'HEAD')
{
$this->_head = TRUE;
}
//-------------------------------------------------
// Set the headers.
//-------------------------------------------------
if($this->_scheme == 'HTTPS')
{
$this->_scheme = 'HTTP';
}
if($this->_scheme != 'UDP')
{
$this->_socketCommand("{$method} {$this->_path} {$this->_scheme}");
}
$this->_socketCommand("Host: {$this->_server}");
$this->_socketCommand("User-Agent: ". REMOTE_XML_USERAGENT);
$this->_socketCommand("X-Requested-With: ". REMOTE_XML_USERAGENT);
$this->_socketCommand("Connection: Close");
$this->_socketCommand("");
//-------------------------------------------------
// Make sure to close the request.
//-------------------------------------------------
register_shutdown_function(array(&$this, 'closeRequest'));
}
//-------------------------------------------------
// Close the socket.
//-------------------------------------------------
function closeRequest()
{
if(!$this->_conn_closed)
{
fclose($this->_conn);
$this->_conn_closed = TRUE;
}
}
//-------------------------------------------------
// Get the data that the socket returned.
//-------------------------------------------------
function getReturnData()
{
$data = "";
$first_line = TRUE;
$getting_headers = TRUE;
//-------------------------------------------------
// Loop through the data returned.
//-------------------------------------------------
while(!feof($this->_conn))
{
$line = fgets($this->_conn, 4096);
//-------------------------------------------------
// This is the first line, check for the 200 code
// (everything's a-okay)
//-------------------------------------------------
if($first_line)
{
if(strlen($line) != 0)
{
//-------------------------------------------------
// Did something go wrong?
//-------------------------------------------------
if(strstr($line, " 200 ") === FALSE)
{
trigger_error("[ERROR] An error occured. HTTP status code was not 200.");
}
$first_line = FALSE;
//-------------------------------------------------
// If we only want to get the headers, make them part
// of the data.
//-------------------------------------------------
if($this->_head)
{
$data .= $line;
}
}
}
//-------------------------------------------------
// If we're not getting headers anymore, add this line to the data.
//-------------------------------------------------
if(!$getting_headers && !$this->_head)
{
$data .= $line;
}
//-------------------------------------------------
// If we're still getting headers and this line is equal
// to CR\LF, tell the script to start recording data for
// the next loop.
//-------------------------------------------------
if($getting_headers && $line == "\r\n")
{
$getting_headers = FALSE;
//-------------------------------------------------
// So, we only want to return the headers.
//-------------------------------------------------
if($this->_head)
{
break;
}
}
}
return $data;
}
//--------------------------------------------
// Send data through the socket.
//--------------------------------------------
function setPutData($xml)
{
if($this->_put)
{
fputs($this->_conn, $xml, strlen($xml));
}
}
//--------------------------------------------
// Send a command to the socket.
//--------------------------------------------
function _socketCommand($command)
{
$command = $command ."\r\n";
fputs($this->_conn, $command, strlen($command));
}
}
?>
Well, you should be able to figure out how to use them, they're pretty straightforward. Have fun with them!
- Development
- PHP
- Applications
- OneLobby by Peter Goodman on Jan 29, 2007 @ 11:32pm

