<?php
# Template for Web Service
# http://path/to/webservice.php?foo=bar&outputformat=json&sign=xxxx
# Based on
# http://php.net/manual/en/function.is-array.php#102652
function is_assoc($array)
{
if (!is_array($array))
return false;
$expectedkey = 0;
foreach ($array as $key => $value) {
if ($key != $expectedkey)
return true;
++$expectedkey;
}
return false;
}
# This solution is based on
# http://php.net/manual/en/book.simplexml.php#101949
# http://php.net/manual/en/book.wddx.php
function xmlencode($var)
{
$xml = new SimpleXMLElement('<xmlencode/>');
$append = function ($append, $node, $item) {
if (is_array($item)) {
if (is_assoc($item)) {
$struct = $node->addChild('struct');
foreach ($item as $itemkey => $itemvalue) {
$var = $struct->addChild('var');
$var->addAttribute('name', $itemkey);
$append($append, $struct, $itemvalue);
}
}
else {
$array = $node->addChild('array');
$array->addAttribute('length', count($item));
foreach ($item as $itemvalue) {
$append($append, $array, $itemvalue);
}
}
}
else {
$node->addChild(gettype($item), $item);
}
};
$append($append, $xml, $var);
return $xml->saveXml();
}
function verifyrequest()
{
$secretkey = '9377d31db4dd92834e2219d88073f5af';
$query = preg_replace('/sign=[[:xdigit:]]{32}/', '',
"{$_SERVER['QUERY_STRING']}{$secretkey}");
if (!isset($_GET['sign']) || $_GET['sign'] != md5($query)) {
header('HTTP/1.1 403 Forbidden');
exit('Access denied');
return false;
}
$GLOBALS['outputformat'] = 'json';
if (isset($_GET['outputformat'])) {
$GLOBALS['outputformat'] = $_GET['outputformat'];
}
return true;
}
function outputresult($result)
{
switch ($GLOBALS['outputformat']) {
case 'json':
# http://stackoverflow.com/questions/477816/the-right-json-content-type#2590013
header('Content-Type: application/json');
exit(json_encode($result));
case 'wddx':
header('Content-Type: text/xml; charset=utf-8');
exit(wddx_serialize_value($result));
case 'xml':
header('Content-Type: text/xml; charset=utf-8');
exit(xmlencode($result));
case 'serialize':
header('Content-Type: text/plain; charset=utf-8');
exit(serialize($result));
default:
header('HTTP/1.1 400 Bad Request');
exit('Invalid Output Format');
}
}
function processrequest()
{
return array(1, 2, 3 => array('a', 'b', 'c'));
}
if (verifyrequest()) {
outputresult(processrequest());
}
?>
Refactorings
No refactoring yet !
Vladimir Barbarosh
April 14, 2011, April 14, 2011 12:16, permalink
I changed the way query string are signed and add example of using service from command line.
<?php
# Template for Web Service
# http://path/to/webservice.php?foo=bar&outputformat=json&sign=xxxx
# bash(1)
# secretkey=00000000000000000000000000000000
# query="foo=bar&outputformat=json"
# sign=$(echo -n "${query}:${secretkey}" | md5sum | cut -d' ' -f1)
# curl --silent --show-error --fail \
# "http://path/to/webservice.php?${query}&sign=${sign}"
# Based on
# http://php.net/manual/en/function.is-array.php#102652
function is_assoc($array)
{
if (!is_array($array))
return false;
$expectedkey = 0;
foreach ($array as $key => $value) {
if ($key != $expectedkey)
return true;
++$expectedkey;
}
return false;
}
# Based on
# http://php.net/manual/en/book.simplexml.php#101949
# http://php.net/manual/en/book.wddx.php
function xmlencode($var)
{
$xml = new SimpleXMLElement('<xmlencode/>');
$append = function ($append, $node, $item) {
if (is_array($item)) {
if (is_assoc($item)) {
$struct = $node->addChild('struct');
foreach ($item as $itemkey => $itemvalue) {
$var = $struct->addChild('var');
$var->addAttribute('name', $itemkey);
$append($append, $struct, $itemvalue);
}
}
else {
$array = $node->addChild('array');
$array->addAttribute('length', count($item));
foreach ($item as $itemvalue) {
$append($append, $array, $itemvalue);
}
}
}
else {
$node->addChild(gettype($item), $item);
}
};
$append($append, $xml, $var);
return $xml->saveXml();
}
function verifyrequest()
{
$secretkey = '00000000000000000000000000000000';
$query = preg_replace('/\&?sign=[[:xdigit:]]{32}/', '',
"{$_SERVER['QUERY_STRING']}:{$secretkey}");
if (!isset($_GET['sign']) || $_GET['sign'] != md5($query)) {
header('HTTP/1.1 403 Forbidden');
exit('Access denied');
return false;
}
$GLOBALS['outputformat'] = 'json';
if (isset($_GET['outputformat'])) {
$GLOBALS['outputformat'] = $_GET['outputformat'];
}
return true;
}
function outputresult($result)
{
switch ($GLOBALS['outputformat']) {
case 'json':
# http://stackoverflow.com/questions/477816/the-right-json-content-type#2590013
header('Content-Type: application/json');
exit(json_encode($result));
case 'wddx':
header('Content-Type: text/xml; charset=utf-8');
exit(wddx_serialize_value($result));
case 'xml':
header('Content-Type: text/xml; charset=utf-8');
exit(xmlencode($result));
case 'serialize':
header('Content-Type: text/plain; charset=utf-8');
exit(serialize($result));
default:
header('HTTP/1.1 400 Bad Request');
exit('Invalid Output Format');
}
}
function processrequest()
{
return array(1, 2, 3 => array('a', 'b', 'c'));
}
if (verifyrequest()) {
outputresult(processrequest());
}
?>
I have no experience building Web Services at all. I build this template in hope that this is right point to start from. Am I right? (Any refactoring and/or comments are welcome.)