Sources for file plugins/class/pavid.php in version 4.0 Beta 1
Click on a comment to hide it. Click here to show all comments.
/**
* Project: Xnyo 4: Bubbles
* File: plugins/class/pavid.php
*
* Version: 4.0-dev
* SVN Id: $Id: pavid.php 5 2007-05-18 03:49:07Z bok $
* SVN URL: $HeadURL:
http://svn.syd.wholesalebroadband.com.au/xnyo/trunk/plugins/class/pavid.php $
* Authors: Robert Amos <bok[at]odynia.org>
*
* Copyright (c) 2001-2007 Robert Amos <bok[at]odynia.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
/**
* Pavid is our XML-RPC stuff
**/
class XnyoPavid implements XnyoClassPlugin
{
// Plugin Information
public $_plugin_info = array
(
'name' => 'Pavid: Xnyo\'s XML-RPC server',
'description' => 'Server side software that listens for incoming XML-RPC requests, and acts
appropriately',
'action' => XNYO_PLUGIN_ACTION_RETURN
);
/**
* Information Storage
**/
private $request;
private $response;
private $xnyo;
private $types;
private $return = array();
private $errors;
/**
* Constructor
**/
public function __construct ()
{
$this->xnyo = $GLOBALS['xnyo_parent'];
}
/**
* Handle bubble extras
**/
public function handleBubbleRequest ()
{
// is this a GET request?
if ($_SERVER['REQUEST_METHOD'] == 'GET')
{
global $bubbles;
// sure is, lets get the details we're looking for.
$file = explode(DIRSEP, XnyoInput::filename(substr($_SERVER['REQUEST_URI'],
strlen($_SERVER['SCRIPT_NAME'])+1)));
// the bubble should be the first item
$bubble = XnyoInput::safetext($file[0]);
if (empty($bubble) || !$bubbles->isBubble($bubble))
exit;
// let the XML error handlers take care of it if its not a valid request
if (count($file) == 3 || count($file) == 4)
{
if (count($file) == 4)
{
$f = $file[3];
$id = XnyoInput::int($file[2]);
} else
{
$f = $file[2];
$id = 0;
}
// the type should therefore be the second item.
switch (strtolower(XnyoInput::safetext($file[1])))
{
case 'script':
case 'scripts':
if (!$bubbles->isScript($bubble, $f))
exit;
header('Content-Type: text/plain');
echo $bubbles->fetchScript($bubble, $f, $id);
exit;
case 'style':
case 'stylesheets':
case 'css':
if (!$bubbles->isStylesheet($bubble, $f))
exit;
header('Content-Type: text/css');
echo $bubbles->fetchStylesheet($bubble, $f, $id);
exit;
}
exit;
} elseif (count($file) == 2)
{
// ok, getting bubble html?
if (substr($file[1], -5) != '.html')
exit;
// the template name should be there then
$tpl = substr($file[1], 0, -5).'.tpl';
if (!$bubbles->isHTML($bubble, $tpl))
exit;
echo $bubbles->fetchHTML($bubble, $tpl);
exit;
}
}
}
/**
* Parse the incoming Pavid Request
**/
public function parse ($xml=null)
{
if (is_null($xml))
{
// is the incoming request XML?
$this->xnyo->fserver('CONTENT_TYPE', 'header');
if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'text/xml')
return $this->error('Incoming Content Type (%s) is not text/xml', $_SERVER['CONTENT_TYPE']);
// have anything?
$this->xnyo->fserver('CONTENT_LENGTH', 'int');
if ($_SERVER['CONTENT_LENGTH'] <= 0)
return $this->error('Incoming XML request body is empty.');
$fp = fopen('php://input', 'r');
$xml = '';
while (!feof($fp))
$xml .= fgets($fp);
fclose($fp);
}
// make sure we have some XML
if (empty($xml))
return $this->error('Supplied or resultant XML request is empty.');
// so we have our XML now, can we parse it?
$this->request = simplexml_load_string($xml);
// did it parse ok?
if ($this->request === false)
return $this->error('Supplied or resultant request is not valid XML.');
}
/**
* Process
*
* Process a parse'd Pavid request
**/
public function process ()
{
if (empty($this->request))
{
return $this->error('No Pavid Request available to process.');
}
if (!is_array($this->types))
$this->types = $this->xnyo->storage->filter->get_valid_types();
/**
* We expect a request to be in the format:
*
* <?xml version="1.0"?>
* <pavid_request>
* <call>
* <method>plugin::method</method>
* <arguments>
* <arg type="<input method>">
* Some stuff
* </arg>
* ...
* </arguments>
* </call>
* ...
* </pavid_request>
**/
// if there are no calls..
if (empty($this->request->call))
return $this->error('No method calls available to be made.');
// loop over the call(s)
foreach ($this->request->call as $num => $call)
{
// see if the call has no method
if (empty($call->method))
$this->add_error('No method found in call %s', $num);
// check our method
$m = explode('::', (string)$call->method);
$c = count($m);
if (!$c || $c < 2 || $c > 3)
$this->add_error('Invalid method in call %s', $num);
// XnyoInput::safetext() all arguments
$m = XnyoInput::_array($m, 'safetext');
// bubble
$type = XNYO_PLUGIN_CLASS;
if ($c == 3 && $m[0] == 'bubble')
{
$type = XNYO_PLUGIN_BUBBLE;
array_shift($m);
$c = 2;
}
// plugin
if ($c == 2)
{
// check to make sure this is a valid plugin
try
{
if (!$this->xnyo->storage->plugins->is_loaded($m[0], $type))
$o = $this->xnyo->load($m[0], $type);
} catch (XnyoError $e)
{
$this->add_error('Provided method call %s is not valid.', (string)$call->method);
$this->xnyo->storage->error->raise($e);
continue;
}
// check to make sure the method exists
if (!is_object($o) || !method_exists($o, $m[1]))
{
$this->add_error('Provided method call %s is not valid.', (string)$call->method);
continue;
}
// check the methods signature
if (!is_array($o->{$m[1]}))
{
$this->add_error('Method %s does not have a Pavid Signature and cannot be called via Pavid.',
(string)$call->method);
continue;
}
// check the methods on the method signature
foreach ($o->{$m[1]} as $type)
if (!in_array($type, $this->types))
{
$this->add_error('Type %s for method %s is not valid.', $type, (string)$call->method);
continue 2;
}
// loop over each argument, filtering them
$i = 0;
$args = array();
if (!empty($call->arguments))
foreach ($call->arguments->arg as $arg)
{
if (!isset($o->{$m[1]}[$i]) || empty ($o->{$m[1]}[$i]))
{
$this->add_error('Too many arguments supplied for %s', (string)$call->method);
continue 2;
}
$t = $o->{$m[1]}[$i];
$args[] = XnyoInput::$t((string)$arg);
$i++;
}
try
{
// all our arguments are now parsed! call the requested method
$r = call_user_func_array(array($o, $m[1]), $args);
if (is_object($r) && $r instanceof PavidEvent)
$this->return[$r->event()] = $r->args();
else
$this->return['response::'.(string)$call->method] = $this->handleBoolean($r);
} catch (PavidError $e) {}
}
}
}
private function handleBoolean ($b)
{
if ($b === true)
return 'true';
elseif ($b === false)
return 'false';
else
return $b;
}
/**
* Return our response(s) as xml
**/
public function respond ($output=true)
{
// merge any session-based requests with this one.
if (isset($_SESSION['_xnyo_pavid_responses']) && is_array($_SESSION['_xnyo_pavid_responses']))
$this->return += $_SESSION['_xnyo_pavid_responses'];
// here we go then!
$xml = '<?xml version="1.0"?><pavid_response>';
// any errors?
if (!empty($this->errors))
{
$xml .= '<errors>';
foreach ($this->errors as $e)
$xml .= '<error>' . $e . '</error>';
$xml .= '</errors>';
}
// any responses?
if (!empty($this->return))
foreach ($this->return as $k => $v)
{
$xml .= '<call><event>'.$k.'</event><arguments>';
foreach ((array)$v as $x)
$xml .= '<arg><![CDATA['.$x.']]></arg>';
$xml .= '</arguments></call>';
}
$xml .= '</pavid_response>';
// output it
if ($output)
{
header('Content-Type: text/xml');
echo $xml;
exit;
}
// return it
return $xml;
}
/**
* Error
**/
public function error ()
{
$args = func_get_args();
$this->errors[] = call_user_func_array('sprintf', $args);
$this->xnyo->storage->error->raise(new XnyoError(call_user_func_array('sprintf', $args)));
$this->respond();
}
public function add_error ()
{
$args = func_get_args();
$this->errors[] = call_user_func_array('sprintf', $args);
$this->xnyo->storage->error->raise(new XnyoError(call_user_func_array('sprintf', $args)));
}
}
/**
* Pavid Event Object
**/
class PavidEvent implements XnyoClassPlugin
{
// Plugin Information
public $_plugin_info = array
(
'name' => 'Pavid Response Object',
'description' => 'Return a new instance of this object to invoke an event in the bubble system',
'action' => XNYO_PLUGIN_ACTION_NONE
);
/**
* Storage
**/
protected $event;
protected $args;
/**
* Constructor
**/
public function __construct ($event, $args)
{
$this->event = $event;
$this->args = (array)$args;
}
/**
* Get the Event
**/
public function event ()
{
return $this->event;
}
/**
* Get the supplied arguments
**/
public function args ()
{
return $this->args;
}
}
class PavidError extends Exception
{
/**
* Constructor
**/
public function __construct ()
{
global $xnyo_parent;
$args = func_get_args();
call_user_func_array(array($xnyo_parent->storage->plugins->get_plugin_reference('pavid',
XNYO_PLUGIN_CLASS), 'add_error'), $args);
}
}
