* @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"; } else { return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val"; } } // 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"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val"; } break; case (is_int($val) || is_long($val) || $type == 'int'): if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val"; } break; case (is_float($val)|| is_double($val) || $type == 'float'): if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val"; } break; case (is_string($val) || $type == 'string'): $val = $this->expandEntities($val); if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val"; } 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.''; 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.""; } 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 .= ""; } 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); } ?>