From: pkiddie <pk...@us...> - 2005-07-28 13:38:10
|
Update of /cvsroot/stack/stack-1-0/scripts/rqp In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19108/scripts/rqp Added Files: Tag: development_xmlrqp RQPv1p0Server.php index.php nb_easyxml_lite.php nb_soapfuncs.php rqp_util.php Log Message: Adding Nialls modified scripts --- NEW FILE: rqp_util.php --- <?php /** * Stack RQP utility functions * Not part of the RQP Server/Client combo but necessary to interact with RQP * * @package stackRQP * @subpackage Stack */ // Utility functions // /** * Converts an associative array of variables into an array of key-value * objects suitable for RQP * * Creates an array of {@link RQPVar} objects from an associative array * of variables. * @param array $vars Associative array of variables. * @return array Returns an array of objects with the key property set to * the key of the corresponding entry in the vars parameter and the values * property set to the corresponding value. */ function MakeRQPVars($vars) { if (!count($vars)) return array(); array_walk($vars, create_function('&$val, $key', '$obj->identifier = $key; $obj->values = (is_array($val) ? array_values($val) : array($val)); $val = $obj;')); return array_values($vars); } /** * Converts an array of name-value objects as used by RQP into an * associative array of variables. * * Creates an associative array of responses from an array of * {@link RQPInput} objects. * @param array $vars Array of {@link RQPInput} objects. * @return array Returns an associative array with the key of each element * set to the name property of the corresponding object and the value set to * the value property of the corresponding object. */ function FlattenRQPInputs($vars) { if (!is_array($vars)) return array(); $return = array(); foreach ($vars as $var) { $var = (object) $var; // sort out nuSOAP encoding weirdness if (!empty($var->name)) { $return[$var->name] = $var->value; } } return $return; } /** * Converts an array of key-value objects as used by RQP into an * associative array of variables. * * Creates an associative array of variables from an array of {@link RQPVar} * objects. * @param array $vars Array of {@link RQPVar} objects. * @return array Returns an associative array with the key of each element * set to the key property of the corresponding object and the value set to * the values property of the corresponding object. */ function FlattenRQPVars($vars) { if (!is_array($vars)) return array(); $return = array(); foreach ($vars as $var) { $var = (object) $var; // sort out nuSOAP encoding weirdness if (!empty($var->identifier)) { if (1 === count($var->values)) $return[$var->identifier] = $var->values[0]; else $return[$var->identifier] = array_values($var->values); } } return $return; } /** * Interprets source for the all operations which need it. * * @param string $source The item source. * @return array $item The item is returned. */ function source_handler($source, &$format, &$errors, $item_information=FALSE) { global $stack_web_url; $item = NULL; if ('' == trim($source)) { // No source supplied return MakeRQPFault(MakeRQPError('emptySource', 'No item source supplied.'), 'No item source supplied.'); } // We need to use the source supplied, and had beter interpret the format. if ('' === $format) { // format not specified; we are asked to make our best guess if (stristr($source,'<assessmentItem>')) { $format = STACK_URI_FORMAT_XML; } else { $format = STACK_URI_FORMAT_FLAT; } } switch ($format) { case STACK_URI_FORMAT_XML: $itemTemp = stack_xml_parse_question_string($source); $item = $itemTemp['assessmentItem']; $questionBody = addslashes(base64_serialize($itemTemp)); break; case STACK_URI_FORMAT_FLAT: $questionBody = $source; $item = stack_db_decodebody($questionBody); break; default: // Invalid format return MakeRQPFault(MakeRQPError('unknownFormat', '`' . $format . '\' is not a format which STACK can use.'), 'Unknown source format.'); } // By this stage we should have an $item. // Validate item $errors = array(); stack_question_validate($item,$errors); // HACK: throwing a SOAP error here will break the itemInformation operation..... if (count($errors) > 0) { // $errstr should be generated on the client side // $errstr = stack_question_errstr($errors); if (!$item_information) return MakeRQPFault(MakeRQPError('invalidSource', 'The item is invalid.', $errors)); } // HACK: Items no longer need GUIDS so just generate one if it is not set if (empty($item['questionGUID'])) { $item['questionGUID'] = stack_generate_guid($stack_web_url); } $item['questionID'] = 0; // For test and RQP questions only return $item; } /** * Creates an RQP error object. * * An {@link RQPError} object is created to represent the error condition. * @param string $id The identifier for the error (this will be prefixed * with the error URI). * @param string $description Human readable description of the error * reprensented by the id. * @param mixed $detail Machine readable information about the error. * @return RQPError The error object created. */ function MakeRQPError($id, $description='', $detail='') { $error = new RQPError; $error->identifier = RQP_URI_ERROR . $id; $error->message = $description; $error->detail = $detail; return $error; } /** * Checks if a value is an RQP error object. * * The value is tested for being an object and if it is an object for being * of class {@link RQPError}. * @param mixed $value The value to be tested. * @return boolean Returns true if the value is an object of class * {@link RQPError}, false otherwise. */ if (floor(phpversion()) > 4) { function IsRQPError($value) { if (is_object($value)) { if ('RQPError' === get_class($value)) { return true; } } return false; } } else { // get_class returns the class name in lowercase under PHP 4 function IsRQPError($value) { if (is_object($value)) { if ('rqperror' === get_class($value)) { return true; } } return false; } } /** * Creates an RQP style SOAP fault object. * * Creates a SOAP fault object with an RQP error object describing the * error using the SOAP detail field. * @param object $error The {@link RQPError} object describing the error. * @param string $description Human readable description of the error * reprensented by the id field of the error object. * @return SoapFault The SOAP fault object. */ function MakeRQPFault($error, $description='') { $errorID = substr($error->id, strlen(RQP_URI_ERROR)); switch ($errorID) { case 'emptySource': case 'unknownFormat': case 'invalidSource': case 'invalidTemplateVariables': $who = 'Client'; break; default: $who = 'Server'; break; } if (empty($description)) $description = $errorID; return make_soap_fault($who, 'RQP error: '.$description, '', $error); } ?> --- NEW FILE: RQPv1p0Server.php --- <?php include("nb_easyxml_lite.php"); include("nb_soapfuncs.php"); include("rqp_util.php"); //Stack/RQP utility functions /********* Data structures used by this web service ********* * * type ServerInformationDType{ * ['identifier']=>anyURI * ['name']=>string * ['description']=>string * ['rqpVersion']=>string * ['studentDocs']=>anyURI * ['teacherDocs']=>anyURI * ['operations']=>array(string, ...) * ['serverProperties']=>array({ * ['key']=>string * ['val']=>string * }, ...) [...1174 lines suppressed...] } else $ret .= $this->soapify_anyURI($input, "anyURI") . "\n"; $ret .= "</$name>\n"; return $ret; } function desoap_anyURIArray($xml, $idx, $name) { $ret = array(); $cidx = $xml->FindChildElement($idx); while($cidx != false) { $ret[] = $this->desoap_anyURI($xml, $cidx, "anyURI"); $cidx = $xml->FindNextPeer($cidx); } } } ?> --- NEW FILE: nb_soapfuncs.php --- <?php /***************************************************************************** The purpose of this library is to provide a very simple, interoperable SOAP implentation for PHP that is not dependent on any optional libraries and can function with both PHP version 4.3 and PHP version 5. This library is expected to be used with generated code from ws_gen and requires nb_easyxml.php. *****************************************************************************/ class nbSOAP { //# this is just a placeholder for now, eventually it will // hold serialization methods for inbuilt types and // the SOAP request functions. } // HTTP 1.1 version; under development, not ready for use function nbSOAP_request11($URI, $SOAPAction, $data) { $contents = ""; $urldata = parse_url($URI); if(!array_key_exists('port', $urldata)) $urldata['port']=80; $request = "POST " . $urldata['path'] . " HTTP/1.1\r\n"; $request .= "Host: ". $urldata['host'] ."\r\n"; $request .= "Accept: */*\r\n"; $request .= "User-Agent: PHP-script\r\n"; $request .= "Content-Type: text/xml; charset=utf-8\r\n"; $request .= "Content-length: " . strlen($data) . "\r\n"; $request .= "SOAPAction: \"" . $SOAPAction . "\"\r\n\r\n"; $request .= $data; $sock = fsockopen($urldata['host'],$urldata['port'], $errno, $errmsg, 30); stream_set_blocking($sock, false); //echo "<pre>".htmlentities($request)."</pre>"; //## if($sock == false) { return false; } else { fputs($sock, $request); //# this part needs to properly check the length of response, and //# time out neatly if there's too long a delay. $cl = strlen($contents); $contents .= fread($sock, 8192); while ((!feof($sock))&&($cl < strlen($contents))) { $cl = strlen($contents); $contents .= fread($sock, 8192); } fclose($sock); } echo "<pre>" . htmlentities($contents) . "</pre>"; return $contents; } function nbSOAP_request10($URI, $SOAPAction, $data) { $contents = ""; $urldata = parse_url($URI); if(!array_key_exists('port', $urldata)) $urldata['port']=80; $request = "POST " . $urldata['path'] . " HTTP/1.0\r\n"; $request .= "Host: ". $urldata['host'] ."\r\n"; $request .= "Accept: */*\r\n"; $request .= "User-Agent: PHP-script\r\n"; $request .= "Content-Type: text/xml; charset=utf-8\r\n"; $request .= "Content-length: " . strlen($data) . "\r\n"; $request .= "SOAPAction: \"" . $SOAPAction . "\"\r\n\r\n"; $request .= $data; $sock = fsockopen($urldata['host'],$urldata['port'], $errno, $errmsg, 30); stream_set_blocking($sock, false); //echo "<pre>".htmlentities($request)."</pre>"; // Uncomment this for debugging if($sock == false) { return false; } else { fputs($sock, $request); while (!feof($sock)) { $contents .= fread($sock, 8192); } fclose($sock); } //echo "<pre>" . htmlentities($contents) . "</pre>"; // Uncomment this for debugging return $contents; } function nbSOAP_Envelope($body) { $body = trim($body); if(substr($body,0,10) != "<soap:Body") return false; $soapXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; $soapXML .= "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "; $soapXML .= "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"; $soapXML .= $body; $soapXML .= "\n</soap:Envelope>\n"; return $soapXML; } ?> --- NEW FILE: nb_easyxml_lite.php --- <?PHP /*!< This set of classes and function is to provide a simple method of * dealing with XML that does not depend on optional libraries */ define("START_TAG",1); define("END_TAG",2); define("CLOSED_TAG",3); define("CONTENT",4); define("XML_ERROR",0); define("XML_DECLARATION",5); // <?xml define("XML_DOCTYPE",6); // <!DOCTYPE ... > define("XML_INSTRUCTION",7); // <? define("XML_CDATA",8); // <[CDATA[ ... ]]> define("XML_COMMENT",9); // <!-- ... --> /*! Base class from which tag and content classes are derived. Never used directly. */ class nb_xml_part{ var $type; //!< Indicates the type of entity - tag, content etc. var $depth; //!< Indicates how deep in the xml tree this entity is. //! A 'pure virtual' function. Derived classes impliment their own version. function dump() { return "<font color='red'>This should never be called!</font><br>"; } }; //! Class for tags in the list of the XML. class nb_xml_tag extends nb_xml_part{ var $tagname; //!< The name of the tag, no namespace prefix var $attribs; //!< An associative array containing attributes keyed by name var $cleansource; //!< the tidy source for the tag. // Next four lines added 28Jun05 NSFB var $nsURI; //!< The namespace URI of the tag if it exists var $nsPrefix; //!< The namespace prefix of the tag if it was used // this is only really meaningful in start elements, potentially used in closed though var $nsDetail; //!< an array of namespaces declared here ['!'] used as prefix for default; /*! Constructor takes a XML tag (without < >) sets $tagname, * $type (START_TAG|END_TAG|CLOSED_TAG), and $attribs, * an associative array of attributes. */ function nb_xml_tag($tagsource) { // initialise to default values $this->nsDetail = array(); $this->nsPrefix = ""; $this->nsURI = false; // "" would be empty, false is undefined // tidy up the ends and make sure the < and > are gone. $tagsource=trim($tagsource); if($tagsource[0]=="<") { $tagsource = substr($tagsource,1); } if($tagsource[strlen($tagsource)-1]==">") { $tagsource = substr($tagsource,0,strlen($tagsource)-1); } $tagsource=trim($tagsource); $this->cleansource=$tagsource; // now check what sort of tag it is... // echo $tagsource[0]; // debug switch($tagsource[0]) { case '?': if(substr($tagsource,0,4)=="?xml"){ $this->type = XML_DECLARATION;} else{ $this->type = XML_INSTRUCTION;} break; case '!': if(substr($tagsource,0,8)=="!DOCTYPE"){ $this->type = XML_DOCTYPE;} elseif(substr($tagsource,0,3)=="!--"){ $this->type = XML_COMMENT;} else{ $this->type = XML_ERROR;} break; case '[': if(substr($tagsource,0,7)=="[CDATA["){ $this->type = XML_CDATA;} else{ $this->type = XML_ERROR;} break; case '/': $this->type = END_TAG; $this->tagname = substr($tagsource,1); // split off namespace part if it exists, added 28Jun05 NSFB if(strpos($this->tagname,":") !== FALSE) list($this->nsPrefix,$this->tagname) = explode(":",$this->tagname,2); else $this->nsPrefix = ""; break; default: //it's a normal tag! if($tagsource[strlen($tagsource)-1]=='/'){ $this->type = CLOSED_TAG; $tagsource = substr($tagsource,0,strlen($tagsource)-1); } else{ $this->type = START_TAG;} // need to extract name and attributes here. if(strpos($tagsource," ")==false) { $nm=$tagsource; $attrs=""; } else list($nm,$attrs)=explode(" ",$tagsource,2); $attrs = preg_replace("/\s+/"," ",$attrs); $attrs = str_replace("= \"","=\"",$attrs); $attrs = str_replace(" =\"","=\"",$attrs); $attrs=trim($attrs); while(strlen($attrs)>2) { if((strpos($attrs," ")!==false)&&((strpos($attrs," ")<strpos($attrs,"=")))) //== would indicate end of attribs, no val { list($attrname,$attrs)=explode(" ",$attrs,2); $attrval=true; } else { list($attrname,$attrs)=explode("=",$attrs,2); $echar=$attrs[0]; $attrs=trim($attrs); if(($echar=="\"")||($echar=="'")) { $attrs = substr($attrs,1); list($attrval,$attrs)=explode($echar,$attrs,2); } else list($attrval,$attrs)=explode(" ",$attrs,2); if($attrval=="") $attrval=true; } $this->attribs[trim($attrname)]=$attrval; $attrs=trim($attrs); // do the namespace collecting stuff, added 28Jun05, NSFB if(strpos($attrname,":")!==false) list($attrname, $prefix) = explode(":",$attrname,2); else $prefix = ""; // so there's a key use this instead of ""; if($attrname=="xmlns") { $this->nsDetail[$prefix] = trim($attrval); } // end of namespace collecting stuff } $this->tagname = $nm; if(strpos($this->tagname,":") !== FALSE) list($this->nsPrefix,$this->tagname) = explode(":",$this->tagname,2); else $this->nsPrefix = ""; // alocate namespace URI if possible , added 28Jun05, NSFB if(array_key_exists($this->nsPrefix,$this->nsDetail)) $this->nsURI = $this->nsDetail[$this->nsPrefix]; break; } } /*! Creates a tidy version of the tag for output. Start, end and closed tags are all correctly formated. Attributes will not nessesaraly be in the original order. \return A string containing the well formated tag. */ function dump() { switch($this->type) { case START_TAG: $rstr = "<".$this->tagname; if(is_array($this->attribs)) { while (list($akey, $aval)=each($this->attribs)) { if(strstr($aval,"\"")===false){ $rstr .= " ".$akey."=\"".$aval."\"";} else{ $rstr .= " ".$akey."='".$aval."'";} } } return $rstr.">"; break; case CLOSED_TAG: $rstr = "<".$this->tagname; if(is_array($this->attribs)) { while (list($akey, $aval)=each($this->attribs)) { if(strstr($aval,"\"")===false){ $rstr .= " ".$akey."=\"".$aval."\"";} else{ $rstr .= " ".$akey."='".$aval."'";} } } else { if(strlen($this->attribs)>0){$rstr .= " ".$this->attribs;} } return $rstr."/>"; break; case END_TAG: return "</".$this->tagname.">"; break; default: return "<".$this->cleansource.">"; break; } } }; /*! Class for content in the XML list - content is everything between tags, but does not include child tags, they become the next item in the list. */ class nb_xml_content extends nb_xml_part{ var $content; /*! constructor takes the content and sets the block type. \param $source is the content. */ function nb_xml_content($source) { $this->content = $source; $this->type = CONTENT; } //! returns the content. function dump() { return $this->content; } } /*! The main class of the XML library reads in XML and provides methods for manipulating it. The XML is held as a list of tags and content, each with a type and depth. */ class nb_easyxml { var $entities; var $source; // C++ private: equiv /*! Get a 'block' of xml from $this->source, either a tag or DATA/CDATA starting * from $point, update point to first char after the 'block' */ function getblock(&$point) { if($this->source[$point]=='<') { //print("<br>tag at $point<br>");//debug switch($this->source[$point+1]) { case '?': $estr = '?'.'>'; // split to be nice to PHP parser; break; case '!': if(substr($this->source,$point+1,3)=="!--"){ $estr = "-->";} elseif($this->source[$point+2]=="["){ $estr = "]]>";} else{ $estr = ">";} break; default: $estr = ">"; break; } $epoint = strpos($this->source,$estr,$point); if($epoint === false) { $point = strlen($this->source); return ""; } $epoint += strlen($estr); $block = substr($this->source,$point,$epoint-$point); $point = $epoint; return $block; } else { $epoint = strpos($this->source,'<',$point); if($epoint === false) { $epoint = strlen($this->source); } $block = substr($this->source,$point,$epoint-$point); $point = $epoint; return $block; } } // C++ protected: equiv /*! Parses XML into the list and does a limited amount of checking. Not really intended to be used from outside the library. \param $source is the xml to be parsed. */ function parsein($source) { $this->entities=""; // this clears out old data. $this->source = $source; $prspt=0; // counter for where in the xml the parser has got to (PaRSe PoinT). $level=0; // how far down the xml tree I am! $nsInfo = array(); // namespace allocation array while($prspt < strlen($this->source)) { $block = $this->getblock($prspt); $printblk = htmlentities($block); // echo "<p><b>".$prspt."</b> ".strlen($block)."<br>".$printblk."</p>";//debug if($block[0] == '<') { $tagcls = new nb_xml_tag($block); $tagcls->depth = $level; if($tagcls->type== START_TAG) { $tagatlev[$level]=$tagcls->tagname; //printf("At level $level got tag '$tagcls->tagname'<br>"); // debug $level++; // store namespace info $nsInfo[$level] = $tagcls->nsDetail; $testlev = $level - 1; // no need to test this level as tag parser does it. while(($tagcls->nsURI === false)&&($testlev >= 0)) { if(array_key_exists($tagcls->nsPrefix, $nsInfo[$testlev])) $tagcls->nsURI = $nsInfo[$testlev][$tagcls->nsPrefix]; $testlev--; } //printf("At level $level got tag '$tagcls->tagname' in namespace {$tagcls->nsURI}<br>"); // debug } $this->entities[] = $tagcls; //$sz = sizeof($this->entities); //debug //echo $this->entities[$sz-1]->depth.": tag - ".$tagcls->tagname."<br>"; // debug if($tagcls->type== END_TAG) { $level--; if($tagatlev[$level]!=$tagcls->tagname) { die("XML error - unmatching start and end tags (/".$tagatlev[$level]." expected, found /".$tagcls->tagname.")"); } } } else { $tblock = trim($block); if($tblock != "") // eliminate all whitespace blocks. { $contcls = new nb_xml_content($block); $contcls->depth = $level; $this->entities[] = $contcls; //echo $contcls->depth.": some content<br>"; // debug } } } } /*! Finds the root tag of the xml. \return the index of the root tag. */ function root() { $index=0; while(($index<=sizeof($this->entities))&&($this->entities[$index]->type!=START_TAG)) { $index++; } if($index<=sizeof($this->entities)){ return $index;} else{ return false;} } /*! Finds the index of the first occurence of a tag after an index point. \param $tagnm is the tag name to search for \param $startpt is the point to start the search from (defaults to 0) \return the index of the first occurence of the tag or false */ function findElement($tagnm, $startpt=0, $namespace = false) { $n = $startpt; $found = false; while(($n < sizeof($this->entities))&&($found===false)) { if(($this->entities[$n]->type==START_TAG)&&($this->entities[$n]->tagname==$tagnm)) { if(($namespace==false)||($namespace==$this->entities[$n]->nsURI)) $found=true; } if($found==false) $n++; } if($found===true){ return $n; } else{ return false;} } function getContent($idx) { if($this->entities[$idx]->type==CLOSED_TAG) return ""; elseif($this->entities[$idx]->type==START_TAG) { $n = $idx+1; $rstr = ""; while(($n < sizeof($this->entities))&&($this->entities[$n]->depth > $this->entities[$idx]->depth)) { if($this->entities[$n]->type==CONTENT) //# need to deal with CDATA too { $rstr .= $this->entities[$n]->dump(); } $n++; } return $rstr; } else return false; } /*! Finds either an immediate child or a peer (same depth) tag. If $tagnm is "" (default) the first child or peer tag is returned, otherwise only one with a matching name. This is the basic function for FindChildTag, FindNextPeerTag etc. This should be considered private, nicer functions exist that call this. \return index of the found tag or false. */ function findclosetag($parent, $tagnm="", $child=false, $namespace=false) { if($parent===false) die("false passed as parent in findclosetag"); $n = $parent+1; $seekdepth=$this->entities[$parent]->depth; //echo "$n parent [$parent], depth $seekdepth;<br>"; if($child) $seekdepth++; $found = false; while(($n < sizeof($this->entities))&&($found===false)&&($this->entities[$n]->depth>=$seekdepth)) { if((($this->entities[$n]->type==START_TAG)|| ($this->entities[$n]->type==CLOSED_TAG)) &&($this->entities[$n]->depth==$seekdepth) &&(($tagnm=="")||($this->entities[$n]->tagname==$tagnm)) &&(($namespace==false)||($namespace==$this->entities[$n]->nsURI)) ){ $found=true;} else{ $n++;} } if($found===true){ return $n; } else{ return false;} } /*! Finds imediate child tags of a particular tag (indicated by index). The tag index returned will either be the first child tag, or the first with a particular name. \param $parent is the index of the tag to find children of. \param $tagnm if provided is the tagname of child to search for. \return the index of the child tag or false */ function findChildElement($parent, $tagnm="", $namespace=false) { return $this->findclosetag($parent, $tagnm, true, $namespace); } /*! Finds the next peer tag of a particular tag (indicated by index) The tag index returned will either be the first peer tag, or the first with a particular name. \param $searchfrom is the index of the tag to search from. \param $tagnm is the tagname of peer to search for - if not given returns the first child index \return the index of the child tag or false */ function findNextPeer($searchfrom, $tagnm="", $namespace=false) { return $this->findclosetag($searchfrom, $tagnm, false, $namespace); } /*! Converts a block into a string of XML. The block goes from the start tag index provided to the matching end tag. \param $tagnum is the index of the start tag of the block to be dumped \return a string containing the block of XML */ function dumpblock($tagnum) { if($tagnum===false) return ""; $n = $tagnum+1; $rstr = $this->entities[$tagnum]->dump(); while(($n < sizeof($this->entities))&&($this->entities[$n]->depth > $this->entities[$tagnum]->depth)) { $rstr .= $this->entities[$n]->dump(); $n++; } return $rstr; } function getName($index) { return $this->entities[$index]->tagname; } function getNamespace($index) { return $this->entities[$index]->nsURI; } function getAttribute($index, $attribname) { if((is_array($this->entities[$index]->attribs))&&(array_key_exists($attribname,$this->entities[$index]->attribs))) return $this->entities[$index]->attribs[$attribname]; else return ""; } }; function nb_easyxmldoc($inp) { $dom = new nb_easyxml; $dom->parsein($inp); return $dom; } ?> --- NEW FILE: index.php --- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html><head><title>RQP 1.0 test</title></head> <body> <?php include("RQPv1p0Client.php"); $rqpserver = new RQPv1p0(); $rqpserver->Url = "http://localhost/RQPv1p0/RQPv1p0Server.php"; $serverInfo = $rqpserver->RQP_ServerInformation(); echo serialize($serverInfo); ?> </body> </html> |