Select Git revision
sws.class.php
sws.class.php 42.16 KiB
<?php
/*
* SIMPLE WEBSERVICE
* <br>
* THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE <br>
* LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR <br>
* OTHER PARTIES PROVIDE THE PROGRAM ?AS IS? WITHOUT WARRANTY OF ANY KIND, <br>
* EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED <br>
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE <br>
* ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. <br>
* SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY <br>
* SERVICING, REPAIR OR CORRECTION.<br>
* <br>
*
* --- HISTORY:<br>
* 2014-04-30 0.5 gui enhancements: input field for each param of all methods
* 2014-04-29 0.4 abstract all data before displaying them
* 2014-04-06 0.3 enable/ disable gui; detect class parameters
* 2014-04-05 0.2
* 2014-04-05 0.1 first public beta
*
* url params
* - class - classname
* - action - method to call [classname] -> [method]
* - args - arguments (as json)
* - type - outputtype (default: json)
*
* TODOs:
* - add param for arguments to initialize the class (detect params of __construct())
* - show an error: detect if a configured method is not public
* - parse parameters from phpdoc - detect required and optional params
* - parse parameters from phpdoc - show input fields for each parameters
* - configuration: option area
* - configuration: examples of a class + of actions
*
* @version 0.06
* @author Axel Hahn
* @link http://www.axel-hahn.de/php_sws
* @license GPL
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0
* @package Axels simple web service
*/
class sws {
/**
* config array with known classnames and its supported methods
* @var string
*/
private $_aKnownClasses = array();
/**
* array of incoming parameters (GET, POST)
* @var array
*/
private $_aParams = array();
/**
* name of the detected classname in the http request
* @var string
*/
private $_sClass = false;
/**
* name of the detected init arguments for the class in the http request
* @var string
*/
private $_aInit = array();
/**
* name of the detected method in the http request
* @var string
*/
private $_sAction = false;
/**
* name of the detected arguments in the http request
* @var string
*/
private $_aArgs = array();
/**
* name of the found class file (by found class it is fetched from config)
* @var string
*/
private $_sClassfile = false;
/**
* relative default dir from sws.class.php to class files in the config
* @var string
*/
private $_sClassDir = "";
//private $_sClassDir = "../php/classes/";
/**
* output type for the respone to the client
* @var string
*/
private $_sOutputType = "json";
/**
* version
* @var string
*/
private $_sVersion = "0.07 (beta)";
/**
* title
* @var string
*/
private $_sTitle = "SWS :: simple web service";
private $_aOptions = array(
'enableGui'=>true,
'enableDump'=>false
);
/**
* url for documentation
* @var string
*/
private $_sUrlDoc = "";
/**
* url of project home
* @var string
*/
private $_sUrlHome = "http://www.axel-hahn.de/php_sws";
// ----------------------------------------------------------------------
// general functions
// ----------------------------------------------------------------------
/**
* constructor
* @param array $aKnownClasses array with known classes and supported methods
* @return boolean
*/
public function __construct($aKnownClasses = false) {
if (is_array($aKnownClasses)) {
$this->setConfig($aKnownClasses);
}
return true;
}
// ----------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------
/**
* apply a given config
* @param array $aKnownClasses array with known classes and supported methods
* @return bool
*/
public function setConfig($aKnownClasses) {
$this->_aKnownClasses = array();
if (is_array($aKnownClasses)) {
$this->_aKnownClasses = $aKnownClasses;
if (array_key_exists("options", $aKnownClasses)){
$this->_aOptions=array_merge($this->_aOptions, $aKnownClasses["options"]);
}
return true;
}
return false;
}
/**
* helper for _parseParams
* check if parameter args contain safe chars
* @param type $sParamValue
* @return boolean
*/
private function _verifyParamValue($sParamValue){
$sOKChars='a-z0-9\"\{\}\[\]\.\,\ \:\-\+\_';
/*
$sOKChars='a-z0-9\"\`\'\{\}\[\]\.\,\ \:\-\+'
.'\<\>\='
;
*/
if(isset($this->_aParams[$sParamValue])){
$sVal=urldecode($this->_aParams[$sParamValue]);
$sBadchars=preg_replace('/['.$sOKChars. ']/i', '',$sVal);
if($sBadchars){
$this->_quit("ERROR: parameter $sParamValue=.. contains unsupported character(s): [". $sBadchars."]");
}
}
return true;
}
/**
* parse parameters (given GET/ POST is in by _aParams @see setParams)
* class - class to initialize
* init - parameters for constructor
* action - public method to call
* args - arguments for the method
* @return bool
*/
private function _parseParams() {
$aMinParams = array("class", "action");
$aMaxParams = array_merge($aMinParams, array("init", "args", "type"));
$sErrors = '';
// set defaults
$this->_sClass = false;
$this->_aInit = array();
$this->_sAction = false;
$this->_aArgs = array();
if (!$this->_aKnownClasses || !is_array($this->_aKnownClasses)) {
$this->_quit("ERROR: no configuration was found."
. "You can set it with initialisation <br>\n"
. "<code>\$o = new " . __CLASS__ . " ( \$aOptions );</code><br>\n"
. "or<br>\n"
. "<code>\$o = new " . __CLASS__ . " ();<br>\n"
. "\$o -> setConfig ( \$aOptions );</code><br>\n"
);
}
// check minimal params
foreach ($aMinParams as $sKey) {
$bFound = array_key_exists($sKey, $this->_aParams);
if (!$bFound) {
$sErrors.="- <em>" . $sKey . "</em><br>";
}
}
// check max Params
foreach(array_keys($this->_aParams) as $sKey){
if(!in_array($sKey, $aMaxParams)){
$this->_quit("ERROR: parameter ". htmlentities($sKey)." is unknown.");
}
}
// check if classname and action exist in configuration
if (array_key_exists("class", $this->_aParams)) {
if (!array_key_exists($this->_aParams["class"], $this->_aKnownClasses["classes"])) {
$this->_quit('ERROR: parameter <em>class = ' . $this->_aParams["class"] . '</em> is invalid. I cannot handle this class.', $this->_showClasshelp());
} else {
// set internal vars: classname
$this->_sClass = $this->_aParams["class"];
$this->_sClassfile = $this->_aKnownClasses["classes"][$this->_aParams["class"]]["file"];
// get arguments for the method
$this->_verifyParamValue("init");
$this->_verifyParamValue("args");
if (array_key_exists("init", $this->_aParams)) {
try {
$aTmp = json_decode($this->_aParams["init"], 1);
} catch (Exception $e) {
// nop
}
if (!is_array($aTmp)) {
$this->_quit(
'ERROR: wrong request - init value must be a json string (in url encoded form)<br>'
. 'examples:<br>'
. '- one arg <code>(...)&init=["my string"]</code><br>'
. '- two args <code>(...)&init=["my string", 123]</code> '
, $this->_showClasshelp());
}
$this->_aInit = $aTmp;
}
}
// get method name to call
if (array_key_exists("action", $this->_aParams)) {
if (!array_key_exists($this->_aParams["action"], $this->_aKnownClasses["classes"][$this->_aParams["class"]]["actions"])) {
$this->_quit('ERROR: class ' . $this->_aParams["class"] . ' exists but it has no <em>action = ' . $this->_aParams["action"] . '</em>.', $this->_showClasshelp());
} else {
// set internal vars
$this->_sAction = $this->_aParams["action"];
}
}
// get arguments for the method
if (array_key_exists("args", $this->_aParams)) {
try {
$aTmp = json_decode($this->_aParams["args"], 1);
} catch (Exception $e) {
// nop
}
if (!is_array($aTmp)) {
$this->_quit(
'ERROR: wrong request - args value must be a json string (in url encoded form)<br>'
. 'examples:<br>'
. '- one arg <code>(...)&args=["my string"]</code><br>'
. '- two args <code>(...)&args=["my string", 123]</code> '
, $this->_showClasshelp());
}
$this->_aArgs = $aTmp;
}
}
if (array_key_exists("type", $this->_aParams)) {
$this->setOutputType($this->_aParams["type"]);
}
if ($sErrors) {
$this->_quit('ERROR: wrong request - a required parameter was not found:<br>' . $sErrors, $this->_showClasshelp());
}
return true;
}
/**
* helper function: parse php doc comment block
* @param string $sComment
* @return string
*/
private function _parseComment($sComment) {
$sReturn = $sComment;
if (!$sReturn) {
return '<div class="warning">WARNING: this object has no PHPDOC comment.</div>';
}
// all @-Tags
$aTags = array(
"abstract", "access", "author", "category", "copyright", "deprecated", "example",
"final", "filesource", "global", "ignore", "internal", "license",
"link", "method", "name", "package", "param", "property", "return",
"see", "since", "static", "staticvar", "subpackage", "todo", "tutorial",
"uses", "var", "version"
);
$sReturn = preg_replace('@[\ \t][\ \t]*@', ' ', $sReturn); // remove multiple spaces
$sReturn = preg_replace('@^\/\*\*.*@', '', $sReturn); // remove first comment line
$sReturn = preg_replace('@.*\*\/@', '', $sReturn); // remove last comment line
$sReturn = preg_replace('@ \* @', '', $sReturn); // remove " * "
$sReturn = preg_replace('@ \*@', '', $sReturn); // remove " *"
$sReturn = preg_replace('@(<br>)*$@', '', $sReturn);
$sReturn = preg_replace('@^\<br\>@', '', $sReturn);
/*
foreach(array("param", "return", "var") as $sKey){
$sReturn=preg_replace('/\@'.$sKey.' ([a-zA-Z\|]*)\ (.*)\<br\>/U', '@'.$sKey.' <span style="font-weight: bold; color:#000;">$1</span> <span style="font-weight: bold;">$2</span><br>', $sReturn);
}
*/
$sReturn = preg_replace('/(\@.*)$/s', '<span class="phpdoc">$1</span>', $sReturn);
foreach ($aTags as $sKey) {
$sReturn = preg_replace('/\@(' . $sKey . ')/U', '<span class="doctag">@' . $sKey . '</span>', $sReturn);
}
if ($sReturn) {
$sReturn = '<pre>' . $sReturn . '</pre><span class="phpdoctitle">PHPDOC</span>';
}
return $sReturn;
}
/**
* quit with error: returns 404 and the given errormessage in the body
* @param type $sError
*/
private function _quit($sError, $sMore = "") {
header("HTTP1.0 400 Bad Request");
echo $this->_wrapAsWebpage(
$sMore, '<div class="error">' . $sError . '</div>'
);
die();
}
/**
* read a class and fetch information about its methods and parameters
*/
private function _collectClassData(){
// loop classes of the given config
foreach (array_keys($this->_aKnownClasses["classes"]) as $sMyClass) {
$RefClass=&$this->_aKnownClasses["classes"][$sMyClass];
require_once($this->_sClassDir . $RefClass["file"]);
$oRefClass = new ReflectionClass($sMyClass);
$RefClass=&$this->_aKnownClasses["classes"][$sMyClass];
$RefClass['phpdoc-class']=$this->_parseComment($oRefClass->getDocComment());
$RefClass['active']=(array_key_exists("class", $this->_aParams) && $sMyClass===$this->_aParams["class"])?true:false;
// loop configured methods of a class
// foreach (array_keys($this->_aKnownClasses["classes"][$sMyClass]["actions"]) as $sAction) {
foreach (array_merge(array("__construct"), array_keys($this->_aKnownClasses["classes"][$sMyClass]["actions"])) as $sAction) {
// foreach (array_keys($this->_aKnownClasses["classes"][$sMyClass]["actions"]) as $sAction) {
$oMethod = false;
try {
$oMethod = $oRefClass->getMethod($sAction);
} catch (Exception $e) {
// nop
echo $e->getMessage();
}
if ($oMethod) {
$bActive=(array_key_exists("action", $this->_aParams) && $sAction===$this->_aParams["action"])?true:false;
$sComment = $this->_parseComment($oRefClass->getMethod($sAction)->getDocComment());
$sActionKey=($sAction==="__construct")?"init":"args";
$aGivenParams=false;
if (array_key_exists($sActionKey, $this->_aParams)){
$aGivenParams=json_decode($this->_aParams[$sActionKey]);
}
if (!$aGivenParams){
$aGivenParams=array();
}
$aParams=array();
$iRequired=$oMethod->getNumberOfRequiredParameters();
// loop params of the method
$iCount=0;
foreach($oMethod->getParameters() as $oParam){
preg_match('@Parameter\ \#.*\[\ (.*)\ \]@', $oParam->__toString(), $aTmp);
preg_match('@(.*)=\ (.*)@', $aTmp[1], $aTmp1);
$aParams[$iCount]["orig"]=$oParam->__toString();
$sValue=false;
if (count($aTmp1)){
$aParams[$iCount]["varname"]=trim($aTmp1[1]);
$sType=$aTmp1[2];
$sValue=$aTmp1[2];
$sValue=str_replace(array("'", '"'), array('', ''), $sValue);
if ($sValue=="Array"){
$sValue='{ }';
}
if ($sValue=="false"){
$sValue='';
}
$aParams[$iCount]["defaultvalue"]=$aTmp1[2];
}
/*
$aParams[$iCount]["required"]=false;
} else {
$aParams[$iCount]["required"]=true;
}
*/
$sValue=(array_key_exists($iCount, $aGivenParams) && $aGivenParams[$iCount])?json_encode($aGivenParams[$iCount]):$sValue;
$aParams[$iCount]["value"]=$sValue;
$aParams[$iCount]["required"]=$iCount<$iRequired ? true:false;
$iCount++;
}
$RefClass["actions"][$sAction]['active']=$bActive;
$RefClass["actions"][$sAction]['phpdoc']=$sComment;
$RefClass["actions"][$sAction]['params']=$aParams;
$RefClass["actions"][$sAction]['value']=($bActive && array_key_exists("args", $this->_aParams))?$this->_aParams["args"]:'';
if($sAction==="__construct"){
$RefClass["actions"][$sAction]['value']=(array_key_exists("init", $this->_aParams))?$this->_aParams["init"]:'';
}
} else {
// '' . $sAction . ' <<< ERROR in configuration: this method does not exist'
}
}
}
}
/**
* get html code for input fields of the parameters of a class
* @param string $sMyClass name of the class
* @param string $sAction name of the method to render
* @return string
*/
private function _showMethodInputForm($sMyClass, $sAction, $sIdPrefixBase){
$sHtml='';
$iCount=0;
$aParams=array();
$aRefMethod=$this->_aKnownClasses['classes'][$sMyClass]['actions'][$sAction];
$sHtml.=$aRefMethod["phpdoc"];
$iCount=count($aRefMethod["params"]);
if ($iCount) {
$sHtml.='<fieldset><legend>Enter each parameter params to generate json: </legend>';
foreach($aRefMethod["params"] as $sKey=>$aParam){
$sIdInput=$sIdPrefixBase.$sKey;
$sFkt='updateMethodVar(\''.$sIdPrefixBase.'\', '.$iCount.')';
$sChanges='onchange="'.$sFkt.'" '
. 'onkeypress="'.$sFkt.'" '
. 'onkeyup="'.$sFkt.'" '
;
$sHtml.='<label for="'.$sIdInput.'">'.$aParam["varname"].' = </label>'
.'<input type="text" id="'.$sIdInput.'" size="30" '.$sChanges.' value="'.str_replace('"', '"', $aParam["value"]).'"/>'
;
if ($aParam["required"]){
$sHtml.=' (*)';
} else {
$sHtml.=' « optional; default is <span class="defaultvalue">'.$aParam["defaultvalue"].'</span><br>';
}
}
$sHtml.='</fieldset><br>';
} else {
$sHtml.='(no parameters)<br>';
}
return $sHtml;
}
/**
* show help - return html code for gui of the given method
* called in _showClasshelp()
* @param string $sMyClass name of the class
* @param string $sAction name of the method to render
* @return string
*/
private function _showActionHelp($sMyClass, $sAction){
$sReturn = '';
$RefClass=&$this->_aKnownClasses["classes"][$sMyClass];
$sStyle = ' style="display: none;"';
$sBtnValue = '+';
$sCssClass = '';
if ($RefClass["actions"][$sAction]["active"]) {
$sCssClass = 'active';
$sStyle = '';
$sBtnValue = '-';
}
$sValInit=array_key_exists('value', $RefClass['actions']['__construct'])?$RefClass['actions']['__construct']['value']:'';
$sValInit=str_replace('"', '"', $sValInit);
if (array_key_exists("params", $RefClass["actions"][$sAction])) {
$sIdDescription = "help-" . $sMyClass . "-" . $sAction;
$sValArgs=array_key_exists('value', $RefClass['actions'][$sAction])?$RefClass['actions'][$sAction]['value']:'';
$sValArgs=str_replace('"', '"', $sValArgs);
$sIdPrefix=md5($sMyClass.$sAction);
$sChanges='onchange="update(\''.$sIdPrefix.'\')" onkeypress="update(\''.$sIdPrefix.'\')" onkeyup="update(\''.$sIdPrefix.'\')" ';
$sReturn .= '<li class="actionname ' . $sCssClass . '">'
. '<h4>'
. '<button onclick="toggleDesciption(\'' . $sIdDescription . '\', this)">' . $sBtnValue . '</button> '
. $sAction
. '</h4>'
. '<div id="' . $sIdDescription . '"' . $sStyle . '>'
. '<input type="hidden" id="'.$sIdPrefix.'class" value="'.$sMyClass.'" />'
. '<input type="hidden" id="'.$sIdPrefix.'action" value="'.$sAction.'" />'
. '<blockquote>'
. '<h5>init parameters of class "'.$sMyClass.'"</h5>'
. '<blockquote>'
. '<code>$o = new '.$sMyClass.' ( ... )</code><br>'
. $this->_showMethodInputForm($sMyClass, "__construct", $sIdPrefix."init")
;
if (count($RefClass['actions']["__construct"]['params'])){
$sReturn .='OR<br><fieldset><legend>json</legend>'
. '<label for="'.$sIdPrefix.'init">init=</label>'
. '<input type="text" id="'.$sIdPrefix.'init" size="80" class="einit" name="init" value="'.$sValInit.'" '.$sChanges.'/><br>'
. '</fieldset>';
}
$sReturn .= '</blockquote>'
. '<h5>parameters of method "'.$sAction.'"</h5>'
. '<blockquote>'
. '<code>$o ->' . $sAction . ' ( ... )</code><br>'
. $this->_showMethodInputForm($sMyClass, $sAction, $sIdPrefix."args");
if (count($RefClass['actions'][$sAction]['params'])){
$sReturn .='OR<br><fieldset><legend>json</legend>'
. '<label for="'.$sIdPrefix.'args">args=</label>'
. '<input type="text" id="'.$sIdPrefix.'args" size="80" name="args" value="'.$sValArgs.'" '.$sChanges.'/><br>'
. '</fieldset>';
}
$sReturn .='</blockquote>'
. '<h5>select output type</h5>'
. '<blockquote>'
. '<select id="'.$sIdPrefix.'type" '.$sChanges.'>';
foreach (array("raw", "json") as $sType) {
$sReturn .= '<option value="'.$sType.'"';
if (array_key_exists("type", $this->_aParams) && $sType === $this->_aParams["type"]){
$sReturn .= ' selected="selected"';
}
$sReturn .= '>'.$sType.'</option>';
}
$sReturn .= '</select>'
. '</blockquote>'
. '<h5>preview:</h5>'
. '<blockquote>'
. '<textarea id="'.$sIdPrefix.'url" cols="120" rows="2" disabled="disabled"></textarea><br><br>'
. '<div id="'.$sIdPrefix.'code"></div>'
. '</blockquote>'
. '<button onclick="run(\''.$sIdPrefix.'\');">go</button>'
. '</blockquote>'
. '</div>'
. '</li>'
;
} else {
$sReturn .= '<li class="actionname error">'
. '' . $sAction . ' <<< ERROR in configuration: this method does not exist'
. '</li>';
}
return $sReturn;
}
/**
* show help - return html gui with list of allowed classes
* @return string
*/
private function _showClasshelp() {
$sReturn = '';
$this->_collectClassData();
$sReturn .= '<h2>Explore</h2>'
. '<p>allowed classes are:</p>'
. '<ul class="classes">';
foreach (array_keys($this->_aKnownClasses["classes"]) as $sMyClass) {
$RefClass=&$this->_aKnownClasses["classes"][$sMyClass];
$sIdDescription = "help-" . $sMyClass . "";
$sClass = '';
$sStyle = ' style="display: none;"';
$sBtnValue = '+';
if ($RefClass["active"]) {
$sClass = 'active';
$sStyle = '';
$sBtnValue = '-';
}
$sReturn .= '<li class="classname ' . $sClass . ' ">'
. '<h3>'
. '<button onclick="toggleDesciption(\'' . $sIdDescription . '\', this)">' . $sBtnValue . '</button> '
. '<a href="?class=' . $sMyClass . '">' . $sMyClass . '</a>'
. '</h3>'
. '<div id="' . $sIdDescription . '"' . $sStyle . '>'
. $RefClass["phpdoc-class"]
. '<p>allowed methods:</p><ul>';
foreach (array_keys($RefClass["actions"]) as $sAction) {
if ($sAction=="__construct"){
continue;
}
$sReturn.=$this->_showActionHelp($sMyClass, $sAction);
}
foreach($RefClass["actions"] as $sAction){
// nop
}
$sReturn .= '</div>'
. '</li>';
}
$sReturn .= '</ul>';
return $sReturn;
}
/**
* render output as html page
* @param string $sBody html body
* @param string $sError error message
* @return string
*/
private function _wrapAsWebpage($sBody = "", $sError = "") {
$sClassSelect = '<span class="urlvalue">[class]</span>';
$sClassInit = '<span class="urlvalue">[initparams]</span>';
$sActionSelect = '<span class="urlvalue">[action]</span>';
$sParamSelect = '<span class="urlvalue">[parameters]</span>';
$sTypeSelect = '<span class="urlvalue">[raw|json]</span>';
$sSyntax = sprintf(
'<pre>?'
. '<span class="urlvar">class</span>=%s'
. '&<span class="urlvar">init</span>=%s'
. '&<span class="urlvar">action</span>=%s'
. '&<span class="urlvar">args</span>=%s'
. '&<span class="urlvar">type</span>=%s'
. '</pre>', $sClassSelect, $sClassInit, $sActionSelect, $sParamSelect, $sTypeSelect
);
/*
if ($this->_sClass){
$sClassSelect='<strong>'.$this->_sClass.'</strong>';
}
if ($this->_sAction){
$sActionSelect='<strong>'.$this->_sAction.'</strong>';
}
if ($this->_aArgs){
$sActionSelect='<strong>'.$this->_aParams["args"].'</strong>';
}
$sSyntax2=sprintf(
'<pre>?<span class="urlvar">class</span>=%s&<span class="urlvar">action</span>=%s&<span class="urlvar">args</span>=%s&<span class="urlvar">type</span>=%s</pre>',
$sClassSelect,
$sActionSelect,
$sParamSelect,
$sTypeSelect
);
if ($sSyntax!=$sSyntax2){
$sSyntax.='<br><br>change it here:<br>'.$sSyntax2;
}
$sSyntax='general syntax:<br>'.$sSyntax;
*/
$sReturn = '<!DOCTYPE html>' . "\n"
. '<html>'
. '<head>'
. '<title>' . $this->_sTitle . '</title>'
. '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />'
. '<style>'
. 'body{background:#aaa; color: #ddd; font-family: verdana,arial; margin: 0;}'
. '#content{padding: 2em; color: #444; background:#f8f8f8;}'
. '#footer{padding: 0 2em; border-top: 0.5em solid #999;}'
. 'a,a:visited{text-decoration: none; color: #000;}'
. 'a.button{border: 2px solid #888; background:#aaa; padding: 0.2em; color:#eee; border-radius: 0.3em;}'
. 'a.try{background:#080; border-color: #0c0;}'
. 'a.try:hover{background:#0b0; border-color: #0c0;}'
. 'button{ width: 2em; height: 2em; background: #44a; border-radius: 0.3em; color:#eee; border: 3px solid #338; font-size: 110%;}'
. 'h1{background:#222; color:#ccc; padding: 0.3em; margin: 0; border-bottom: 0.2em solid #999;}'
. 'h1 a, h1 a:visited{color:#888;}'
. 'h1 a span{color:#666; font-size: 45%;}'
. 'h2{margin: 3em 0 1em; }'
. 'h3,h4{margin: 0;}'
. 'fieldset{border: 1px solid rgba(0,0,0,0.2); background: rgba(0,0,0,0.05); }'
. 'input {border: 0px solid #ccc; padding: 0.1em; border-radius: 0.2em; color: #348; font-size: 100%;}'
. 'label{width: 10em; float: left; text-align: right;}'
. 'ul{padding-left: 0; margin: 0; border-radius: 0.5em;}'
. 'ul ul{margin: 1em;}'
. '.classes li{list-style: none; border-radius: 0.3em; padding: 0.3em; margin-bottom: 0.3em;}'
. 'li{border-left: 5px solid #aaa; }'
. 'li.active{border-left: 5px solid #aaa; }'
. 'li li.active{border-left: 5px solid #c00; background: #fcc;}'
. 'pre{margin: 0; border: #ddd solid 1px; border-radius: 0.5em; padding: 0.5em; background: #fff; opacity: 0.7;}'
. 'pre .urlvar{color: #480; font-weight: bold;}'
. 'pre .urlvalue{color: #8c0;}'
. 'pre pre{display: inline; padding: 0; border: 0;}'
. 'select {border: 2px solid #ddd; padding: 0.3em; border-radius: 0.5em; color: #348; font-size: 100%;}'
. 'td{color:#444;}'
. '.classname{ background:#eee; }'
. '.actionname{background:#ddd; background: rgba(0,0,0,0.1);}'
. '.phpdoctitle{display: none; background:#ccc; color:#fff;float: right; margin-right: 0.5em; padding: 0 1em; border-radius: 0.5em; border-top-left-radius: 0;border-top-right-radius: 0;}'
. '.phpdoc{background:#f0f4f8;}'
. '.doctag{ color:#080; }'
. '.error{ color:#800; background:#fcc; padding: 0.5em; margin-bottom: 2em; border-left: 4px solid;}'
. '.warning{ color:#a96; background:#fc8; padding: 0.5em; margin-bottom: 2em; border-left: 4px solid;}'
. '.defaultvalue{color: #33c;}'
. '</style>'
;
if($this->_aOptions["enableGui"]){
$sReturn.= ''
. '<script>'
. 'function toggleDesciption(sId, a){'
. 'var o=document.getElementById(sId);'
. 'if (o) {'
. 'o.style.display=(o.style.display=="")?"none":"";'
. '}'
. 'console.log(a);'
. 'if (a) {'
. 'a.innerHTML=(a.innerHTML=="+")?"-":"+";'
. '}'
. '}'
.'
function updateMethodVar(sIdPrefix, iCount){
var sOut=\'\';
var sVal=false;
var aParams=[];
if (!document.getElementById(sIdPrefix)){
return false;
}
for (var i=0; i<iCount; i++) {
sVal=document.getElementById(sIdPrefix+""+i).value;
if(!sVal){
sVal="";
} else {
if (sVal % 1 == 0){
sVal=sVal/1;
} else {
if( sVal === "true" || sVal === "false" ){
sVal=sVal || sVal;
} else {
if (
sVal.indexOf(\'"\')<0
&& sVal.indexOf("\'")<0
&& sVal.indexOf("\[")<0
&& sVal.indexOf("\]")<0
&& sVal.indexOf("\{")<0
&& sVal.indexOf("\}")<0
){
sVal=\'"\'+sVal+\'"\';
}
}
}
}
if (sOut){
sOut+=", ";
}
sOut+=sVal;
aParams[i]=sVal;
}
document.getElementById(sIdPrefix).value="[" + sOut + "]";
// document.getElementById(sIdPrefix).value=JSON.stringify(aParams);
document.getElementById(sIdPrefix).onchange();
}
function update(sIdPrefix){
if (!document.getElementById(sIdPrefix+"class")){
return false;
}
var sUrl="?";
var arguments=["class", "init", "action", "args", "type"];
for (var i=0; i<arguments.length; i++){
if (document.getElementById(sIdPrefix+arguments[i])
&& document.getElementById(sIdPrefix+arguments[i]).value
){
sUrl+="&"+arguments[i]+"="+document.getElementById(sIdPrefix+arguments[i]).value;
}
}
document.getElementById(sIdPrefix+"url").value=sUrl;
return true;
}
function run(sIdPrefix){
if (update(sIdPrefix)) {
location.href=document.getElementById(sIdPrefix+"url").value;
}
return true;
}
'
. '</script>'
;
}
$sReturn.= ''
. '</head>'
. '<body>';
if($this->_aOptions["enableGui"]){
$sReturn.=
'<h1><a href="?">' . $this->_sTitle . ' <span>'.$this->_sVersion.'</span></a></h1>'
. '<div id="content">'
. $sError;
if($this->_aOptions["enableDump"]){
$sReturn.= '<h2>Debug</h2>'
. '_aParams:<pre>'.print_r($this->_aParams, true).'</pre>'
. '_aKnownClasses:<pre>'.print_r($this->_aKnownClasses, true).'</pre>'
;
}
$sReturn.= $sBody
. '<h2>Syntax</h2>'
. $sSyntax
. '</div><div id="footer">'
. '<h2>About</h2>'
. '<p>'
. 'SWS is a free wrapper for other php classes and makes their public '
. 'functions (or a subset of them) available as a webservice.<br>'
. 'It offers a webinterface to explore available classes and methods. '
. 'The shown information is parsed by reading phpdoc.'
. '</p>'
. '<p>'
. 'GNU GPL 3.0;<br>';
if ($this->_sUrlDoc) {
$sReturn.='<a href="' . $this->_sUrlDoc . '">' . $this->_sUrlDoc . '</a> ';
}
if ($this->_sUrlHome) {
$sReturn.='<a href="' . $this->_sUrlHome . '">' . $this->_sUrlHome . '</a> ';
}
$sReturn.='</p></div>';
} else {
$sReturn.=
'<div id="content" style="height: 1000px; overflow: hidden;">'
.$sError .''. $this->_sTitle
.'</div>'
;
}
$sReturn.='</body></html>';
return $sReturn;
}
// ----------------------------------------------------------------------
// setter
// ----------------------------------------------------------------------
/**
* set ouput type of response
* @param string $sOutputType one of json|raw
* @return bool
*/
public function setOutputType($sOutputType) {
$this->_sOutputType = $sOutputType;
return true;
}
/**
* put query parameter
* @param array $aParams parameters; default false and params will be taken from $_GET and $_POST
* @return array
*/
public function setParams($aParams = false) {
$this->_aParams = array();
if (is_array($aParams)) {
$this->_aParams = $aParams;
} else {
if (isset($_GET) && count($_GET)) {
foreach ($_GET as $key => $value)
$this->_aParams[$key] = $value;
}
if (isset($_POST) && count($_POST)) {
foreach ($_POST as $key => $value)
$this->_aParams[$key] = $value;
}
}
$this->_parseParams();
return $this->_aParams;
}
// ----------------------------------------------------------------------
// actions
// ----------------------------------------------------------------------
/**
* execute function
* @param type $sOutputType
*/
public function run($sOutputType = false) {
if (!count($this->_aParams)){
$this->setParams();
}
$this->_parseParams();
require_once($this->_sClassDir . $this->_sClassfile);
if (!class_exists($this->_sClass)) {
$this->_quit("ERROR: class does not exist: " . $this->_sClass);
}
// ------------------------------------------------------------
// init object
// ------------------------------------------------------------
switch (count($this->_aInit)) {
case 0: $o = new $this->_sClass;
break;
case 1: $o = new $this->_sClass($this->_aInit[0]);
break;
case 2: $o = new $this->_sClass($this->_aInit[0], $this->_aInit[1]);
break;
case 3: $o = new $this->_sClass($this->_aInit[0], $this->_aInit[1], $this->_aInit[2]);
break;
case 4: $o = new $this->_sClass($this->_aInit[0], $this->_aInit[1], $this->_aInit[2], $this->_aInit[3]);
break;
default: die("internal ERROR in run(): need to set a new case with up to " . count($this->_aInit) . " arguments in " . __FILE__);
}
if (!method_exists($o, $this->_sAction)) {
$this->_quit("ERROR: method does not exist: " . $this->_sClass . " -> " . $this->_sAction . "()");
}
// ------------------------------------------------------------
// call function
// ------------------------------------------------------------
switch (count($this->_aArgs)) {
case 0: $return = call_user_func(array($o, $this->_sAction));
break;
case 1: $return = call_user_func(array($o, $this->_sAction), $this->_aArgs[0]);
break;
case 2: $return = call_user_func(array($o, $this->_sAction), $this->_aArgs[0], $this->_aArgs[1]);
break;
case 3: $return = call_user_func(array($o, $this->_sAction), $this->_aArgs[0], $this->_aArgs[1], $this->_aArgs[2]);
break;
case 4: $return = call_user_func(array($o, $this->_sAction), $this->_aArgs[0], $this->_aArgs[1], $this->_aArgs[2], $this->_aArgs[3]);
break;
default: die("internal ERROR in run(): need to set a new case with up to " . count($this->_aArgs) . " arguments in " . __FILE__);
}
/*
if (!$return) {
$this->_quit("ERROR: no output");
}
*
*/
// ------------------------------------------------------------
// output
// ------------------------------------------------------------
switch ($this->_sOutputType) {
case "json":
header('Content-Type: application/json');
echo json_encode($return);
break;
case "raw":
if (is_array($return)) {
$this->_quit("ERROR: Wrong output type for that method. Return of " . $this->_sClass . '->' . $this->_sAction . "() is an array", $this->_showClasshelp());
}
echo $return;
break;
default:
$this->_quit("ERROR in " . __FUNCTION__ . ": outputtype </em>" . $this->_sOutputType . "</em> is unknown", $this->_showClasshelp());
}
}
}
// ----------------------------------------------------------------------