* @version $Id: class.nusoap_base.php 46 2007-06-19 07:29:11Z tsigo $
* @access public
*/
class nusoap_base {
var $title = 'NuSOAP';
var $version = '0.6.9';
var $revision = '$Revision: 46 $';
var $error_str = '';
var $debug_str = '';
// toggles automatic encoding of special characters as entities
// (should always be true, I think)
var $charencoding = true;
/**
* set schema version
*
* @var XMLSchemaVersion
* @access public
*/
var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
/**
* set charset encoding for outgoing messages
*
* @var soap_defencoding
* @access public
*/
//var $soap_defencoding = 'UTF-8';
var $soap_defencoding = 'ISO-8859-1';
/**
* load namespace uris into an array of uri => prefix
*
* @var namespaces
* @access public
*/
var $namespaces = array(
'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
'xsd' => 'http://www.w3.org/2001/XMLSchema',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
'si' => 'http://soapinterop.org/xsd');
var $usedNamespaces = array();
/**
* load types into typemap array
* is this legacy yet?
* no, this is used by the xmlschema class to verify type => namespace mappings.
* @var typemap
* @access public
*/
var $typemap = array(
'http://www.w3.org/2001/XMLSchema' => array(
'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
// abstract "any" types
'anyType'=>'string','anySimpleType'=>'string',
// derived datatypes
'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
'http://www.w3.org/2000/10/XMLSchema' => array(
'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
'float'=>'double','dateTime'=>'string',
'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
'http://www.w3.org/1999/XMLSchema' => array(
'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
'float'=>'double','dateTime'=>'string',
'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
'http://xml.apache.org/xml-soap' => array('Map')
);
/**
* entities to convert
*
* @var xmlEntities
* @access public
*/
var $xmlEntities = array('quot' => '"','amp' => '&',
'lt' => '<','gt' => '>','apos' => "'");
/**
* adds debug data to the instance debug string with formatting
*
* @param string $string debug data
* @access private
*/
function debug($string){
$this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
}
/**
* adds debug data to the instance debug string without formatting
*
* @param string $string debug data
* @access private
*/
function appendDebug($string){
// it would be nice to use a memory stream here to use
// memory more efficiently
$this->debug_str .= $string;
}
/**
* clears the current debug data for this instance
*
* @access public
*/
function clearDebug() {
// it would be nice to use a memory stream here to use
// memory more efficiently
$this->debug_str = '';
}
/**
* gets the current debug data for this instance
*
* @return debug data
* @access public
*/
function &getDebug() {
// it would be nice to use a memory stream here to use
// memory more efficiently
return $this->debug_str;
}
/**
* gets the current debug data for this instance as an XML comment
* this may change the contents of the debug data
*
* @return debug data as an XML comment
* @access public
*/
function &getDebugAsXMLComment() {
// it would be nice to use a memory stream here to use
// memory more efficiently
while (strpos($this->debug_str, '--')) {
$this->debug_str = str_replace('--', '- -', $this->debug_str);
}
return "";
}
/**
* expands entities, e.g. changes '<' to '<'.
*
* @param string $val The string in which to expand entities.
* @access private
*/
function expandEntities($val) {
if ($this->charencoding) {
$val = str_replace('&', '&', $val);
$val = str_replace("'", ''', $val);
$val = str_replace('"', '"', $val);
$val = str_replace('<', '<', $val);
$val = str_replace('>', '>', $val);
}
return $val;
}
/**
* returns error string if present
*
* @return mixed error string or false
* @access public
*/
function getError(){
if($this->error_str != ''){
return $this->error_str;
}
return false;
}
/**
* sets error string
*
* @return boolean $string error string
* @access private
*/
function setError($str){
$this->error_str = $str;
}
/**
* detect if array is a simple array or a struct (associative array)
*
* @param $val The PHP array
* @return string (arraySimple|arrayStruct)
* @access private
*/
function isArraySimpleOrStruct($val) {
$keyList = array_keys($val);
foreach ($keyList as $keyListValue) {
if (!is_int($keyListValue)) {
return 'arrayStruct';
}
}
return 'arraySimple';
}
/**
* serializes PHP values in accordance w/ section 5. Type information is
* not serialized if $use == 'literal'.
*
* @return string
* @access public
*/
function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
if(is_object($val) && get_class($val) == 'soapval'){
return $val->serialize($use);
}
$this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
// if no name, use item
$name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
// if name has ns, add ns prefix to name
$xmlns = '';
if($name_ns){
$prefix = 'nu'.rand(1000,9999);
$name = $prefix.':'.$name;
$xmlns .= " xmlns:$prefix=\"$name_ns\"";
}
// if type is prefixed, create type prefix
if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
// need to fix this. shouldn't default to xsd if no ns specified
// w/o checking against typemap
$type_prefix = 'xsd';
} elseif($type_ns){
$type_prefix = 'ns'.rand(1000,9999);
$xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
}
// serialize attributes if present
$atts = '';
if($attributes){
foreach($attributes as $k => $v){
$atts .= " $k=\"$v\"";
}
}
// serialize null value
if (is_null($val)) {
if ($use == 'literal') {
// TODO: depends on nillable
return "<$name$xmlns $atts/>";
} else {
return "<$name$xmlns $atts xsi:nil=\"true\"/>";
}
}
// serialize if an xsd built-in primitive type
if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
if (is_bool($val)) {
if ($type == 'boolean') {
$val = $val ? 'true' : 'false';
} elseif (! $val) {
$val = 0;
}
} else if (is_string($val)) {
$val = $this->expandEntities($val);
}
if ($use == 'literal') {
return "<$name$xmlns $atts>$val$name>";
} else {
return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val$name>";
}
}
// detect type and serialize
$xml = '';
switch(true) {
case (is_bool($val) || $type == 'boolean'):
if ($type == 'boolean') {
$val = $val ? 'true' : 'false';
} elseif (! $val) {
$val = 0;
}
if ($use == 'literal') {
$xml .= "<$name$xmlns $atts>$val$name>";
} else {
$xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val$name>";
}
break;
case (is_int($val) || is_long($val) || $type == 'int'):
if ($use == 'literal') {
$xml .= "<$name$xmlns $atts>$val$name>";
} else {
$xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val$name>";
}
break;
case (is_float($val)|| is_double($val) || $type == 'float'):
if ($use == 'literal') {
$xml .= "<$name$xmlns $atts>$val$name>";
} else {
$xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val$name>";
}
break;
case (is_string($val) || $type == 'string'):
$val = $this->expandEntities($val);
if ($use == 'literal') {
$xml .= "<$name$xmlns $atts>$val$name>";
} else {
$xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val$name>";
}
break;
case is_object($val):
$name = get_class($val);
foreach(get_object_vars($val) as $k => $v){
$pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
}
$xml .= '<'.$name.'>'.$pXml.''.$name.'>';
break;
break;
case (is_array($val) || $type):
// detect if struct or array
$valueType = $this->isArraySimpleOrStruct($val);
if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
$i = 0;
if(is_array($val) && count($val)> 0){
foreach($val as $v){
if(is_object($v) && get_class($v) == 'soapval'){
$tt_ns = $v->type_ns;
$tt = $v->type;
} elseif (is_array($v)) {
$tt = $this->isArraySimpleOrStruct($v);
} else {
$tt = gettype($v);
}
$array_types[$tt] = 1;
$xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
++$i;
}
if(count($array_types) > 1){
$array_typename = 'xsd:ur-type';
} elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
if ($tt == 'integer') {
$tt = 'int';
}
$array_typename = 'xsd:'.$tt;
} elseif(isset($tt) && $tt == 'arraySimple'){
$array_typename = 'SOAP-ENC:Array';
} elseif(isset($tt) && $tt == 'arrayStruct'){
$array_typename = 'unnamed_struct_use_soapval';
} else {
// if type is prefixed, create type prefix
if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
$array_typename = 'xsd:' . $tt;
} elseif ($tt_ns) {
$tt_prefix = 'ns' . rand(1000, 9999);
$array_typename = "$tt_prefix:$tt";
$xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
} else {
$array_typename = $tt;
}
}
$array_type = $i;
if ($use == 'literal') {
$type_str = '';
} else if (isset($type) && isset($type_prefix)) {
$type_str = " xsi:type=\"$type_prefix:$type\"";
} else {
$type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
}
// empty array
} else {
if ($use == 'literal') {
$type_str = '';
} else if (isset($type) && isset($type_prefix)) {
$type_str = " xsi:type=\"$type_prefix:$type\"";
} else {
$type_str = " xsi:type=\"SOAP-ENC:Array\"";
}
}
$xml = "<$name$xmlns$type_str$atts>".$xml."$name>";
} else {
// got a struct
if(isset($type) && isset($type_prefix)){
$type_str = " xsi:type=\"$type_prefix:$type\"";
} else {
$type_str = '';
}
if ($use == 'literal') {
$xml .= "<$name$xmlns $atts>";
} else {
$xml .= "<$name$xmlns$type_str$atts>";
}
foreach($val as $k => $v){
// Apache Map
if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
$xml .= '- ';
$xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
$xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
$xml .= '
';
} else {
$xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
}
}
$xml .= "$name>";
}
break;
default:
$xml .= 'not detected, got '.gettype($val).' for '.$val;
break;
}
return $xml;
}
/**
* serialize message
*
* @param string body
* @param string headers optional
* @param array namespaces optional
* @param string style optional (rpc|document)
* @param string use optional (encoded|literal)
* @return string message
* @access public
*/
function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded'){
// TODO: add an option to automatically run utf8_encode on $body and $headers
// if $this->soap_defencoding is UTF-8. Not doing this automatically allows
// one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
// serialize namespaces
$ns_string = '';
foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
$ns_string .= " xmlns:$k=\"$v\"";
}
if($style == 'rpc' && $use == 'encoded') {
$ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
}
// serialize headers
if($headers){
$headers = "".$headers."";
}
// serialize envelope
return
'soap_defencoding .'"?'.">".
'".
$headers.
"".
$body.
"".
"";
}
function formatDump($str){
$str = htmlspecialchars($str);
return nl2br($str);
}
/**
* contracts a qualified name
*
* @param string $string qname
* @return string contracted qname
* @access private
*/
function contractQname($qname){
// get element namespace
//$this->xdebug("Contract $qname");
if (strrpos($qname, ':')) {
// get unqualified name
$name = substr($qname, strrpos($qname, ':') + 1);
// get ns
$ns = substr($qname, 0, strrpos($qname, ':'));
$p = $this->getPrefixFromNamespace($ns);
if ($p) {
return $p . ':' . $name;
}
return $qname;
} else {
return $qname;
}
}
/**
* expands a qualified name
*
* @param string $string qname
* @return string expanded qname
* @access private
*/
function expandQname($qname){
// get element prefix
if(strpos($qname,':') && !ereg('^http://',$qname)){
// get unqualified name
$name = substr(strstr($qname,':'),1);
// get ns prefix
$prefix = substr($qname,0,strpos($qname,':'));
if(isset($this->namespaces[$prefix])){
return $this->namespaces[$prefix].':'.$name;
} else {
return $qname;
}
} else {
return $qname;
}
}
/**
* returns the local part of a prefixed string
* returns the original string, if not prefixed
*
* @param string
* @return string
* @access public
*/
function getLocalPart($str){
if($sstr = strrchr($str,':')){
// get unqualified name
return substr( $sstr, 1 );
} else {
return $str;
}
}
/**
* returns the prefix part of a prefixed string
* returns false, if not prefixed
*
* @param string
* @return mixed
* @access public
*/
function getPrefix($str){
if($pos = strrpos($str,':')){
// get prefix
return substr($str,0,$pos);
}
return false;
}
/**
* pass it a prefix, it returns a namespace
* returns false if no namespace registered with the given prefix
*
* @param string
* @return mixed
* @access public
*/
function getNamespaceFromPrefix($prefix){
if (isset($this->namespaces[$prefix])) {
return $this->namespaces[$prefix];
}
//$this->setError("No namespace registered for prefix '$prefix'");
return false;
}
/**
* returns the prefix for a given namespace (or prefix)
* or false if no prefixes registered for the given namespace
*
* @param string
* @return mixed
* @access public
*/
function getPrefixFromNamespace($ns) {
foreach ($this->namespaces as $p => $n) {
if ($ns == $n || $ns == $p) {
$this->usedNamespaces[$p] = $n;
return $p;
}
}
return false;
}
/**
* returns the time in ODBC canonical form with microseconds
*
* @return string
* @access public
*/
function getmicrotime() {
if (function_exists('gettimeofday')) {
$tod = gettimeofday();
$sec = $tod['sec'];
$usec = $tod['usec'];
} else {
$sec = time();
$usec = 0;
}
return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
}
function varDump($data) {
ob_start();
var_dump($data);
$ret_val = ob_get_contents();
ob_end_clean();
return $ret_val;
}
}
// XML Schema Datatype Helper Functions
//xsd:dateTime helpers
/**
* convert unix timestamp to ISO 8601 compliant date string
*
* @param string $timestamp Unix time stamp
* @access public
*/
function timestamp_to_iso8601($timestamp,$utc=true){
$datestr = date('Y-m-d\TH:i:sO',$timestamp);
if($utc){
$eregStr =
'([0-9]{4})-'. // centuries & years CCYY-
'([0-9]{2})-'. // months MM-
'([0-9]{2})'. // days DD
'T'. // separator T
'([0-9]{2}):'. // hours hh:
'([0-9]{2}):'. // minutes mm:
'([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
'(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
if(ereg($eregStr,$datestr,$regs)){
return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
}
return false;
} else {
return $datestr;
}
}
/**
* convert ISO 8601 compliant date string to unix timestamp
*
* @param string $datestr ISO 8601 compliant date string
* @access public
*/
function iso8601_to_timestamp($datestr){
$eregStr =
'([0-9]{4})-'. // centuries & years CCYY-
'([0-9]{2})-'. // months MM-
'([0-9]{2})'. // days DD
'T'. // separator T
'([0-9]{2}):'. // hours hh:
'([0-9]{2}):'. // minutes mm:
'([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
'(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
if(ereg($eregStr,$datestr,$regs)){
// not utc
if($regs[8] != 'Z'){
$op = substr($regs[8],0,1);
$h = substr($regs[8],1,2);
$m = substr($regs[8],strlen($regs[8])-2,2);
if($op == '-'){
$regs[4] = $regs[4] + $h;
$regs[5] = $regs[5] + $m;
} elseif($op == '+'){
$regs[4] = $regs[4] - $h;
$regs[5] = $regs[5] - $m;
}
}
return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
} else {
return false;
}
}
function usleepWindows($usec)
{
$start = gettimeofday();
do
{
$stop = gettimeofday();
$timePassed = 1000000 * ($stop['sec'] - $start['sec'])
+ $stop['usec'] - $start['usec'];
}
while ($timePassed < $usec);
}
?>