diff --git a/library/SOAP/Disco.php b/library/SOAP/Disco.php index 618e63d..d71eb37 100644 --- a/library/SOAP/Disco.php +++ b/library/SOAP/Disco.php @@ -1,383 +1,383 @@ * @author Chuck Hagenbuch * @author Jan Schneider * @copyright 2003-2005 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ require_once 'SOAP/Base.php'; class SOAP_DISCO_Server extends SOAP_Base_Object { var $namespaces = array(SCHEMA_WSDL => 'wsdl', SCHEMA_SOAP => 'soap'); var $import_ns = array(); var $wsdl = ''; var $disco = ''; var $_wsdl = array(); var $_disco = array(); var $_service_name = ''; var $_service_ns = ''; var $_service_desc = ''; var $_portname = ''; var $_bindingname = ''; var $soap_server = NULL; function SOAP_DISCO_Server($soap_server, $service_name, $service_desc = '', $import_ns = null) { parent::SOAP_Base_Object('Server'); if ( !is_object($soap_server) || !get_class($soap_server) == 'soap_server') return; $this->_service_name = $service_name; $this->_service_ns = "urn:$service_name"; $this->_service_desc = $service_desc; $this->import_ns = isset($import_ns) ? $import_ns : $this->import_ns; $this->soap_server = $soap_server; $this->host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; } function getDISCO() { $this->_generate_DISCO(); return $this->disco; } function getWSDL() { $this->_generate_WSDL(); return $this->wsdl; } function _generate_DISCO() { // DISCO $this->_disco['disco:discovery']['attr']['xmlns:disco'] = SCHEMA_DISCO; $this->_disco['disco:discovery']['attr']['xmlns:scl'] = SCHEMA_DISCO_SCL; $this->_disco['disco:discovery']['scl:contractRef']['attr']['ref'] = (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] == 'on') ? 'https://' . $this->host . $_SERVER['PHP_SELF'] . '?wsdl' : 'http://' . $this->host . $_SERVER['PHP_SELF'] . '?wsdl'; // generate disco xml $this->_generate_DISCO_XML($this->_disco); } function _generate_WSDL() { // WSDL if (is_array($this->soap_server->_namespaces)) { // need to get: typens, xsd & SOAP-ENC $flipped = array_flip($this->soap_server->_namespaces); $this->namespaces[$this->_service_ns] = 'tns'; $this->namespaces[$flipped['xsd']] = 'xsd'; $this->namespaces[$flipped['SOAP-ENC']] = 'SOAP-ENC'; } // DEFINITIONS $this->_wsdl['definitions']['attr']['name'] = $this->_service_name; $this->_wsdl['definitions']['attr']['targetNamespace'] = $this->_service_ns; foreach ($this->namespaces as $ns => $prefix) { $this->_wsdl['definitions']['attr']['xmlns:' . $prefix] = $ns; } $this->_wsdl['definitions']['attr']['xmlns'] = SCHEMA_WSDL; // Import namespaces. Seems to not work yet: wsdl.exe fom .NET can't // handle imported complete wsdl-definitions. if (count($this->import_ns)) { $i = 0; foreach ($this->import_ns as $_ns => $_location) { $this->_wsdl['definitions']['import'][$i]['attr']['location'] = $_location; $this->_wsdl['definitions']['import'][$i]['attr']['namespace'] = $_ns; $i++; } } $this->_wsdl['definitions']['types']['attr']['xmlns']='http://schemas.xmlsoap.org/wsdl/'; $this->_wsdl['definitions']['types']['schema']=array(); // Placeholder for messages $this->_wsdl['definitions']['message'] = array(); // PORTTYPE-NAME $this->_portname = $this->_service_name . 'Port'; $this->_wsdl['definitions']['portType']['attr']['name'] = $this->_portname; // BINDING-NAME $this->_bindingname = $this->_service_name . 'Binding'; $this->_wsdl['definitions']['binding']['attr']['name'] = $this->_bindingname; $this->_wsdl['definitions']['binding']['attr']['type'] = 'tns:' . $this->_portname; $this->_wsdl['definitions']['binding']['soap:binding']['attr']['style'] = 'rpc'; $this->_wsdl['definitions']['binding']['soap:binding']['attr']['transport'] = SCHEMA_SOAP_HTTP; // SERVICE $this->_wsdl['definitions']['service']['attr']['name'] = $this->_service_name . 'Service'; $this->_wsdl['definitions']['service']['documentation']['attr'] = ''; $this->_wsdl['definitions']['service']['documentation'] = htmlentities($this->_service_desc); $this->_wsdl['definitions']['service']['port']['attr']['name'] = $this->_portname; $this->_wsdl['definitions']['service']['port']['attr']['binding'] = 'tns:' . $this->_bindingname; $this->_wsdl['definitions']['service']['port']['soap:address']['attr']['location'] = (array_key_exists('HTTPS', $_SERVER) && $_SERVER['HTTPS'] == 'on') ? 'https://' . $this->host . $_SERVER['PHP_SELF'] : 'http://' . $this->host . $_SERVER['PHP_SELF']; // $dispatch_keys = array_keys($this->soap_server->dispatch_objects); $dc = count($dispatch_keys); for ($di = 0; $di < $dc; $di++) { $namespace = $dispatch_keys[$di]; $namespace_objects =& $this->soap_server->dispatch_objects[$namespace]; $oc = count($namespace_objects); for ($oi = 0; $oi < $oc; $oi++) { $object = $namespace_objects[$oi]; // types definitions $this->addSchemaFromMap($object->__typedef); // MESSAGES $this->addMethodsFromMap($object->__dispatch_map, $namespace, get_class($object)); } } if (isset($this->soap_server->dispatch_map)) { $this->addMethodsFromMap($this->soap_server->dispatch_map, $namespace); } // generate wsdl $this->_generate_WSDL_XML(); } function &_getSchema($namespace) { // SCHEMA $c = count($this->_wsdl['definitions']['types']['schema']); for($i = 0; $i < $c; $i++) { if ($this->_wsdl['definitions']['types']['schema'][$i]['attr']['targetNamespace'] == $namespace) { return $this->_wsdl['definitions']['types']['schema'][$i]; } } // don't have this namespace $schema = array(); $schema['attr'] = array(); $schema['complexType'] = array(); $schema['attr']['xmlns'] = array_search('xsd',$this->namespaces); $schema['attr']['targetNamespace'] = $namespace; $this->_wsdl['definitions']['types']['schema'][] =& $schema; return $schema; } function addSchemaFromMap(&$map) { if (!$map) { return; } foreach ($map as $_type_name => $_type_def) { list($typens,$type) = $this->_getTypeNs($_type_name); if ($typens == 'xsd') { // cannot add to xsd, lets use method_namespace $typens = 'tns'; } $schema =& $this->_getSchema(array_search($typens, $this->namespaces)); if (!$this->_ifComplexTypeExists($schema['complexType'], $type)) { $ctype =& $schema['complexType'][]; $ctype['attr']['name'] = $type; foreach ($_type_def as $_varname => $_vartype) { if (!is_int($_varname)) { list($_vartypens,$_vartype) = $this->_getTypeNs($_vartype); $ctype['all']['attr'] = ''; $el =& $ctype['all']['element'][]; $el['attr']['name'] = $_varname; $el['attr']['type'] = $_vartypens . ':' . $_vartype; } else { $ctype['complexContent']['attr'] = ''; $ctype['complexContent']['restriction']['attr']['base'] = 'SOAP-ENC:Array'; foreach ($_vartype as $array_type) { list($_vartypens, $_vartype) = $this->_getTypeNs($array_type); $ctype['complexContent']['restriction']['attribute']['attr']['ref'] = 'SOAP-ENC:arrayType'; $ctype['complexContent']['restriction']['attribute']['attr']['wsdl:arrayType'] = $_vartypens . ':' . $_vartype . '[]'; } } } } } } function addMethodsFromMap(&$map, $namespace, $classname = null) { if (!$map) { return; } foreach ($map as $method_name => $method_types) { if (array_key_exists('namespace', $method_types)) { $method_namespace = $method_types['namespace']; } else { $method_namespace = $namespace; } // INPUT $input_message = array('attr' => array('name' => $method_name . 'Request')); if (isset($method_types['in']) && is_array($method_types['in'])) { foreach ($method_types['in'] as $name => $type) { list($typens, $type) = $this->_getTypeNs($type); $part = array(); $part['attr']['name'] = $name; $part['attr']['type'] = $typens . ':' . $type; $input_message['part'][] = $part; } } $this->_wsdl['definitions']['message'][] = $input_message; // OUTPUT $output_message = array('attr' => array('name' => $method_name . 'Response')); if (isset($method_types['out']) && is_array($method_types['out'])) { foreach ($method_types['out'] as $name => $type) { list($typens, $type) = $this->_getTypeNs($type); $part = array(); $part['attr']['name'] = $name; $part['attr']['type'] = $typens . ':' . $type; $output_message['part'][] = $part; } } $this->_wsdl['definitions']['message'][] = $output_message; // PORTTYPES $operation = array(); $operation['attr']['name'] = $method_name; // INPUT $operation['input']['attr']['message'] = 'tns:' . $input_message['attr']['name']; // OUTPUT $operation['output']['attr']['message'] = 'tns:' . $output_message['attr']['name']; $this->_wsdl['definitions']['portType']['operation'][] = $operation; // BINDING $binding = array(); $binding['attr']['name'] = $method_name; $action = $method_namespace . '#' . ($classname ? $classname . '#' : '') . $method_name; $binding['soap:operation']['attr']['soapAction'] = $action; // INPUT $binding['input']['attr'] = ''; $binding['input']['soap:body']['attr']['use'] = 'encoded'; $binding['input']['soap:body']['attr']['namespace'] = $method_namespace; $binding['input']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING; // OUTPUT $binding['output']['attr'] = ''; $binding['output']['soap:body']['attr']['use'] = 'encoded'; $binding['output']['soap:body']['attr']['namespace'] = $method_namespace; $binding['output']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING; $this->_wsdl['definitions']['binding']['operation'][] = $binding; } } function _generate_DISCO_XML($disco_array) { $disco = ''; foreach ($disco_array as $key => $val) { $disco .= $this->_arrayToNode($key,$val); } $this->disco = $disco; } function _generate_WSDL_XML() { $wsdl = ''; foreach ($this->_wsdl as $key => $val) { $wsdl .= $this->_arrayToNode($key, $val); } $this->wsdl = $wsdl; } function _arrayToNode($node_name = '', $array) { $return = ''; if (is_array($array)) { // we have a node if there's key 'attr' if (array_key_exists('attr',$array)) { $return .= "<$node_name"; if (is_array($array['attr'])) { foreach ($array['attr'] as $attr_name => $attr_value) { $return .= " $attr_name=\"$attr_value\""; } } // unset 'attr' and proceed other childs... unset($array['attr']); if (count($array) > 0) { $i = 0; foreach ($array as $child_node_name => $child_node_value) { $return .= $i == 0 ? ">\n" : ''; $return .= $this->_arrayToNode($child_node_name,$child_node_value); $i++; } $return .= "\n"; } else { $return .= " />\n"; } } else { // we have no 'attr' key in array - so it's list of nodes with // the same name ... foreach ($array as $child_node_name => $child_node_value) { $return .= $this->_arrayToNode($node_name,$child_node_value); } } } else { // $array is not an array if ($array !='') { // and its not empty $return .= "<$node_name>$array\n"; } else { // and its empty... $return .= "<$node_name />\n"; } } return $return; } function _getTypeNs($type) { preg_match_all("'\{(.*)\}'sm", $type, $m); if (isset($m[1][0]) && $m[1][0] != '') { if (!array_key_exists($m[1][0],$this->namespaces)) { $ns_pref = 'ns' . count($this->namespaces); $this->namespaces[$m[1][0]] = $ns_pref; $this->_wsdl['definitions']['attr']['xmlns:' . $ns_pref] = $m[1][0]; } $typens = $this->namespaces[$m[1][0]]; - $type = ereg_replace($m[0][0],'',$type); + $type = preg_replace('/'.$m[0][0].'/','',$type); } else { $typens = 'xsd'; } return array($typens,$type); } function _ifComplexTypeExists($typesArray, $type_name) { if (is_array($typesArray)) { foreach ($typesArray as $type_data) { if ($type_data['attr']['name'] == $type_name) { return true; } } } return false; } } diff --git a/library/SOAP/Type/duration.php b/library/SOAP/Type/duration.php index 078e4a4..5365ce8 100644 --- a/library/SOAP/Type/duration.php +++ b/library/SOAP/Type/duration.php @@ -1,165 +1,165 @@ \ No newline at end of file +?> diff --git a/library/SOAP/WSDL.php b/library/SOAP/WSDL.php index e4f774d..b37dd60 100644 --- a/library/SOAP/WSDL.php +++ b/library/SOAP/WSDL.php @@ -1,2280 +1,2280 @@ Original Author * @author Shane Caraveo Port to PEAR and more * @author Chuck Hagenbuch Maintenance * @author Jan Schneider Maintenance * @copyright 2003-2005 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ require_once 'SOAP/Base.php'; require_once 'SOAP/Fault.php'; require_once 'HTTP/Request.php'; define('WSDL_CACHE_MAX_AGE', 43200); /** * This class parses WSDL files, and can be used by SOAP::Client to properly * register soap values for services. * * Originally based on SOAPx4 by Dietrich Ayala * http://dietrich.ganx4.com/soapx4 * * @todo * - add wsdl caching * - refactor namespace handling ($namespace/$ns) * - implement IDL type syntax declaration so we can generate WSDL * * @access public * @package SOAP * @author Shane Caraveo Conversion to PEAR and updates * @author Dietrich Ayala Original Author */ class SOAP_WSDL extends SOAP_Base { var $tns = null; var $definition = array(); var $namespaces = array(); var $ns = array(); var $xsd = SOAP_XML_SCHEMA_VERSION; var $complexTypes = array(); var $elements = array(); var $messages = array(); var $portTypes = array(); var $bindings = array(); var $imports = array(); var $services = array(); var $service = ''; /** * URL to WSDL file. * * @var string */ var $uri; /** * Parse documentation in the WSDL? * * @var boolean */ var $docs; /** * Proxy parameters. * * @var array */ var $proxy; /** * Enable tracing in the generated proxy class? * * @var boolean */ var $trace = false; /** * Use WSDL cache? * * @var boolean */ var $cacheUse; /** * WSDL cache directory. * * @var string */ var $cacheDir; /** * Cache maximum lifetime (in seconds). * * @var integer */ var $cacheMaxAge; /** * Class to use for WSDL parsing. Can be overridden for special cases, * subclasses, etc. * * @var string */ var $wsdlParserClass = 'SOAP_WSDL_Parser'; /** * Reserved PHP keywords. * * @link http://www.php.net/manual/en/reserved.php * * @var array */ var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case', 'catch', 'cfunction', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exception', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'if', 'implements', 'include', 'include_once', 'interface', 'isset', 'list', 'new', 'old_function', 'or', 'php_user_filter', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'this', 'throw', 'try', 'unset', 'use', 'var', 'while', 'xor'); /** * Regular expressions for invalid PHP labels. * * @link http://www.php.net/manual/en/language.variables.php. * * @var string */ var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/'); /** * SOAP_WSDL constructor. * * @param string $wsdl_uri URL to WSDL file. * @param array $proxy Options for HTTP_Request class * @see HTTP_Request. * @param boolean|string $cacheUse Use WSDL caching? The cache directory * if a string. * @param integer $cacheMaxAge Cache maximum lifetime (in seconds). * @param boolean $docs Parse documentation in the WSDL? * * @access public */ function SOAP_WSDL($wsdl_uri = false, $proxy = array(), $cacheUse = false, $cacheMaxAge = WSDL_CACHE_MAX_AGE, $docs = false) { parent::SOAP_Base('WSDL'); $this->uri = $wsdl_uri; $this->proxy = $proxy; $this->cacheUse = !empty($cacheUse); $this->cacheMaxAge = $cacheMaxAge; $this->docs = $docs; if (is_string($cacheUse)) { $this->cacheDir = $cacheUse; } if ($wsdl_uri) { if (!PEAR::isError($this->parseURL($wsdl_uri))) { reset($this->services); $this->service = key($this->services); } } } /** * @deprecated Use setService(). */ function set_service($service) { $this->setService($service); } /** * Sets the service currently to be used. * * @param string $service An (existing) service name. */ function setService($service) { if (array_key_exists($service, $this->services)) { $this->service = $service; } } /** * Fills the WSDL array tree with data from a WSDL file. * * @param string $wsdl_uri URL to WSDL file. */ function parseURL($wsdl_uri) { - $parser =new $this->wsdlParserClass($wsdl_uri, $this, $this->docs); + $parser = new $this->wsdlParserClass($wsdl_uri, $this, $this->docs); if ($parser->fault) { $this->_raiseSoapFault($parser->fault); } } /** * Fills the WSDL array tree with data from one or more PHP class objects. * * @param mixed $wsdl_obj An object or array of objects to add to * the internal WSDL tree. * @param string $targetNamespace The target namespace of schema types * etc. * @param string $service_name Name of the WSDL service. * @param string $service_desc Optional description of the WSDL * service. */ function parseObject(&$wsdl_obj, $targetNamespace, $service_name, $service_desc = '') { - $parser =new SOAP_WSDL_ObjectParser($wsdl_obj, $this, + $parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this, $targetNamespace, $service_name, $service_desc); if ($parser->fault) { $this->_raiseSoapFault($parser->fault); } } function getEndpoint($portName) { if ($this->_isfault()) { return $this->_getfault(); } return (isset($this->services[$this->service]['ports'][$portName]['address']['location'])) ? $this->services[$this->service]['ports'][$portName]['address']['location'] : $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri); } function _getPortName($operation, $service) { if (isset($this->services[$service]['ports'])) { $ports = $this->services[$service]['ports']; foreach ($ports as $port => $portAttrs) { $type = $ports[$port]['type']; if ($type == 'soap' && isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) { return $port; } } } return null; } /** * Finds the name of the first port that contains an operation of name * $operation. Always returns a SOAP portName. */ function getPortName($operation, $service = null) { if ($this->_isfault()) { return $this->_getfault(); } if (!$service) { $service = $this->service; } if (isset($this->services[$service]['ports'])) { if ($portName = $this->_getPortName($operation, $service)) { return $portName; } } // Try any service in the WSDL. foreach ($this->services as $serviceName => $service) { if (isset($this->services[$serviceName]['ports'])) { if ($portName = $this->_getPortName($operation, $serviceName)) { $this->service = $serviceName; return $portName; } } } return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri); } function getOperationData($portName, $operation) { if ($this->_isfault()) { return $this->_getfault(); } if (!isset($this->services[$this->service]['ports'][$portName]['binding']) || !($binding = $this->services[$this->service]['ports'][$portName]['binding'])) { return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri); } // Get operation data from binding. if (is_array($this->bindings[$binding]['operations'][$operation])) { $opData = $this->bindings[$binding]['operations'][$operation]; } // get operation data from porttype $portType = $this->bindings[$binding]['type']; if (!$portType) { return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri); } if (is_array($type = $this->portTypes[$portType][$operation])) { if (isset($type['parameterOrder'])) { $opData['parameterOrder'] = $type['parameterOrder']; } $opData['input'] = array_merge($opData['input'], $type['input']); $opData['output'] = array_merge($opData['output'], $type['output']); } if (!$opData) return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri); $opData['parameters'] = false; if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace'])) $opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace']; // Message data from messages. $inputMsg = $opData['input']['message']; if (is_array($this->messages[$inputMsg])) { foreach ($this->messages[$inputMsg] as $pname => $pattrs) { if ($opData['style'] == 'document' && $opData['input']['use'] == 'literal' && $pname == 'parameters') { $opData['parameters'] = true; $opData['namespace'] = $this->namespaces[$pattrs['namespace']]; $el = $this->elements[$pattrs['namespace']][$pattrs['type']]; if (isset($el['elements'])) { foreach ($el['elements'] as $elname => $elattrs) { $opData['input']['parts'][$elname] = $elattrs; } } } else { $opData['input']['parts'][$pname] = $pattrs; } } } $outputMsg = $opData['output']['message']; if (is_array($this->messages[$outputMsg])) { foreach ($this->messages[$outputMsg] as $pname => $pattrs) { if ($opData['style'] == 'document' && $opData['output']['use'] == 'literal' && $pname == 'parameters') { $el = $this->elements[$pattrs['namespace']][$pattrs['type']]; if (isset($el['elements'])) { foreach ($el['elements'] as $elname => $elattrs) { $opData['output']['parts'][$elname] = $elattrs; } } } else { $opData['output']['parts'][$pname] = $pattrs; } } } return $opData; } function matchMethod(&$operation) { if ($this->_isfault()) { return $this->_getfault(); } // Overloading lowercases function names :( foreach ($this->services[$this->service]['ports'] as $portAttrs) { foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) { if (strcasecmp($op, $operation) == 0) { $operation = $op; } } } } /** * Given a datatype, what function handles the processing? * * This is used for doc/literal requests where we receive a datatype, and * we need to pass it to a method in out server class. * * @param string $datatype * @param string $namespace * @return string * @access public */ function getDataHandler($datatype, $namespace) { // See if we have an element by this name. if (isset($this->namespaces[$namespace])) { $namespace = $this->namespaces[$namespace]; } if (isset($this->ns[$namespace])) { $nsp = $this->ns[$namespace]; //if (!isset($this->elements[$nsp])) // $nsp = $this->namespaces[$nsp]; if (isset($this->elements[$nsp][$datatype])) { $checkmessages = array(); // Find what messages use this datatype. foreach ($this->messages as $messagename => $message) { foreach ($message as $part) { if ($part['type'] == $datatype) { $checkmessages[] = $messagename; break; } } } // Find the operation that uses this message. foreach($this->portTypes as $porttype) { foreach ($porttype as $opname => $opinfo) { foreach ($checkmessages as $messagename) { if ($opinfo['input']['message'] == $messagename) { return $opname; } } } } } } return null; } function getSoapAction($portName, $operation) { if ($this->_isfault()) { return $this->_getfault(); } if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) { return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction']; } return false; } function getNamespace($portName, $operation) { if ($this->_isfault()) { return $this->_getfault(); } if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) { return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']; } return false; } function getNamespaceAttributeName($namespace) { /* If it doesn't exist at first, flip the array and check again. */ if (empty($this->ns[$namespace])) { $this->ns = array_flip($this->namespaces); } /* If it doesn't exist now, add it. */ if (empty($this->ns[$namespace])) { return $this->addNamespace($namespace); } return $this->ns[$namespace]; } function addNamespace($namespace) { if (!empty($this->ns[$namespace])) { return $this->ns[$namespace]; } $n = count($this->ns); $attr = 'ns' . $n; $this->namespaces['ns' . $n] = $namespace; $this->ns[$namespace] = $attr; return $attr; } function _validateString($string) { return preg_match('/^[\w_:#\/]+$/', $string); } function _addArg(&$args, &$argarray, $argname) { if ($args) { $args .= ', '; } $args .= '$' . $argname; if (!$this->_validateString($argname)) { return; } if ($argarray) { $argarray .= ', '; } $argarray .= "'$argname' => $" . $argname; } function _elementArg(&$args, &$argarray, &$_argtype, $_argname) { $comments = ''; $el = $this->elements[$_argtype['namespace']][$_argtype['type']]; $tns = isset($this->ns[$el['namespace']]) ? $this->ns[$el['namespace']] : $_argtype['namespace']; if (!empty($el['complex']) || (isset($el['type']) && isset($this->complexTypes[$tns][$el['type']]))) { // The element is a complex type. $comments .= " // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n"; $attrname = "{$_argtype['type']}_attr"; if (isset($el['type']) && isset($this->complexTypes[$tns][$el['type']]['attribute'])) { $comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n"; } $comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n"; $comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n"; $this->_addArg($args, $argarray, $_argtype['type']); if (isset($el['type']) && isset($this->complexTypes[$tns][$el['type']]['attribute'])) { if ($args) { $args .= ', '; } $args .= '$' . $attrname; } } elseif (isset($el['elements'])) { foreach ($el['elements'] as $ename => $element) { $comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" . (isset($element['type']) ? $element['type'] : false) . "', \$$ename);\n"; $this->_addArg($args, $argarray, $ename); } } else { $comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n"; $this->_addArg($args, $argarray, $_argname); } return $comments; } function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname) { $comments = ''; if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) { $comments = " // $_argname is a ComplexType {$_argtype['type']},\n" . " // refer to wsdl for more info\n"; if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) { $comments .= " // $_argname may require attributes, refer to wsdl for more info\n"; } $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type']; $comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n"; } $this->_addArg($args, $argarray, $_argname); return $comments; } /** * Generates stub code from the WSDL that can be saved to a file or eval'd * into existence. */ function generateProxyCode($port = '', $classname = '') { if ($this->_isfault()) { return $this->_getfault(); } $multiport = count($this->services[$this->service]['ports']) > 1; if (!$port) { reset($this->services[$this->service]['ports']); $port = current($this->services[$this->service]['ports']); } // XXX currently do not support HTTP ports if ($port['type'] != 'soap') { return null; } // XXX currentPort is BAD $clienturl = $port['address']['location']; if (!$classname) { if ($multiport || $port) { $classname = 'WebService_' . $this->service . '_' . $port['name']; } else { $classname = 'WebService_' . $this->service; } $classname = $this->_sanitize($classname); } if (!$this->_validateString($classname)) { return null; } if (is_array($this->proxy) && count($this->proxy)) { $class = "class $classname extends SOAP_Client\n{\n" . " function $classname(\$path = '$clienturl')\n {\n" . " \$this->SOAP_Client(\$path, 0, 0,\n" . ' array('; foreach ($this->proxy as $key => $val) { if (is_array($val)) { $class .= "'$key' => array("; foreach ($val as $key2 => $val2) { $class .= "'$key2' => '$val2', "; } $class .= ')'; } else { $class .= "'$key' => '$val', "; } } $class .= "));\n }\n"; $class = str_replace(', ))', '))', $class); } else { $class = "class $classname extends SOAP_Client\n{\n" . " function $classname(\$path = '$clienturl')\n {\n" . " \$this->SOAP_Client(\$path, 0);\n" . " }\n"; } // Get the binding, from that get the port type. $primaryBinding = $port['binding']; $primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding); $portType = $this->bindings[$primaryBinding]['type']; $portType = preg_replace("/^(.*:)/", '', $portType); $style = $this->bindings[$primaryBinding]['style']; // XXX currentPortType is BAD foreach ($this->portTypes[$portType] as $opname => $operation) { $binding = $this->bindings[$primaryBinding]['operations'][$opname]; if (isset($binding['soapAction'])) { $soapaction = $binding['soapAction']; } else { $soapaction = null; } if (isset($binding['style'])) { $opstyle = $binding['style']; } else { $opstyle = $style; } $use = $binding['input']['use']; if ($use == 'encoded') { $namespace = $binding['input']['namespace']; } else { $bindingType = $this->bindings[$primaryBinding]['type']; $ns = $this->portTypes[$bindingType][$opname]['input']['namespace']; $namespace = $this->namespaces[$ns]; } $args = ''; $argarray = ''; $comments = ''; $wrappers = ''; foreach ($operation['input'] as $argname => $argtype) { if ($argname == 'message') { foreach ($this->messages[$argtype] as $_argname => $_argtype) { $_argname = $this->_sanitize($_argname); if ($opstyle == 'document' && $use == 'literal' && $_argtype['name'] == 'parameters') { // The type or element refered to is used for // parameters. $elattrs = null; $el = $this->elements[$_argtype['namespace']][$_argtype['type']]; if ($el['complex']) { $namespace = $this->namespaces[$_argtype['namespace']]; // XXX need to wrap the parameters in a // SOAP_Value. } if (isset($el['elements'])) { foreach ($el['elements'] as $elname => $elattrs) { $elname = $this->_sanitize($elname); // Is the element a complex type? if (isset($this->complexTypes[$elattrs['namespace']][$elname])) { $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname); } else { $this->_addArg($args, $argarray, $elname); } } } if ($el['complex'] && $argarray) { $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name']; $comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n"; $argarray = "'{$el['name']}' => \${$el['name']}"; } } else { if (isset($_argtype['element'])) { // Element argument. $comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']); } else { // Complex type argument. $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname); } } } } } // Validate entries. // Operation names are function names, so try to make sure it's // legal. This could potentially cause collisions, but let's try // to make everything callable and see how many problems that // causes. $opname_php = $this->_sanitize($opname); if (!$this->_validateString($opname_php)) { return null; } if ($argarray) { $argarray = "array($argarray)"; } else { $argarray = 'null'; } $class .= " function &$opname_php($args)\n {\n$comments$wrappers" . " \$result = \$this->call('$opname',\n" . " \$v = $argarray,\n" . " array('namespace' => '$namespace',\n" . " 'soapaction' => '$soapaction',\n" . " 'style' => '$opstyle',\n" . " 'use' => '$use'" . ($this->trace ? ",\n 'trace' => true" : '') . "));\n" . " return \$result;\n" . " }\n"; } $class .= "}\n"; return $class; } function generateAllProxies() { $proxycode = ''; foreach (array_keys($this->services[$this->service]['ports']) as $key) { $port =& $this->services[$this->service]['ports'][$key]; $proxycode .= $this->generateProxyCode($port); } return $proxycode; } function &getProxy($port = '', $name = '') { if ($this->_isfault()) { $fault =& $this->_getfault(); return $fault; } $multiport = count($this->services[$this->service]['ports']) > 1; if (!$port) { reset($this->services[$this->service]['ports']); $port = current($this->services[$this->service]['ports']); } if ($multiport || $port) { $classname = 'WebService_' . $this->service . '_' . $port['name']; } else { $classname = 'WebService_' . $this->service; } if ($name) { $classname = $name . '_' . $classname; } $classname = $this->_sanitize($classname); if (!class_exists($classname)) { $proxy = $this->generateProxyCode($port, $classname); require_once 'SOAP/Client.php'; eval($proxy); } - $proxy =new $classname; + $proxy = new $classname; return $proxy; } /** * Sanitizes a SOAP value, method or class name so that it can be used as * a valid PHP identifier. Invalid characters are converted into * underscores and reserved words are prefixed with an underscore. * * @param string $name The identifier to sanitize. * * @return string The sanitized identifier. */ function _sanitize($name) { $name = preg_replace($this->_invalid, '_', $name); if (in_array($name, $this->_reserved)) { $name = '_' . $name; } return $name; } function &_getComplexTypeForElement($name, $namespace) { $t = null; if (isset($this->ns[$namespace]) && isset($this->elements[$this->ns[$namespace]][$name]['type'])) { $type = $this->elements[$this->ns[$namespace]][$name]['type']; $ns = $this->elements[$this->ns[$namespace]][$name]['namespace']; if (isset($this->complexTypes[$ns][$type])) { $t = $this->complexTypes[$ns][$type]; } } return $t; } function getComplexTypeNameForElement($name, $namespace) { $t = $this->_getComplexTypeForElement($name, $namespace); if ($t) { return $t['name']; } return null; } function getComplexTypeChildType($ns, $name, $child_ns, $child_name) { // Is the type an element? $t = $this->_getComplexTypeForElement($name, $ns); if ($t) { // No, get it from complex types directly. if (isset($t['elements'][$child_name]['type'])) return $t['elements'][$child_name]['type']; } elseif (isset($this->ns[$ns]) && isset($this->elements[$this->ns[$ns]][$name]['complex']) && $this->elements[$this->ns[$ns]][$name]['complex']) { // Type is not an element but complex. return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type']; } return null; } function getSchemaType($type, $name, $type_namespace) { // see if it's a complex type so we can deal properly with // SOAPENC:arrayType. if ($name && $type) { // XXX TODO: // look up the name in the wsdl and validate the type. foreach ($this->complexTypes as $types) { if (isset($types[$type])) { if (isset($types[$type]['type'])) { list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type]['arrayType']) ? $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']) : array($this->namespaces[$types[$type]['namespace']], null, 0); return array($types[$type]['type'], $arraytype, $arraytype_ns, $array_depth); } if (isset($types[$type]['arrayType'])) { list($arraytype_ns, $arraytype, $array_depth) = $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']); return array('Array', $arraytype, $arraytype_ns, $array_depth); } if (!empty($types[$type]['elements'][$name])) { $type = $types[$type]['elements']['type']; return array($type, null, $this->namespaces[$types[$type]['namespace']], null); } break; } } } if ($type && $type_namespace) { $arrayType = null; // XXX TODO: // this code currently handles only one way of encoding array // types in wsdl need to do a generalized function to figure out // complex types $p = $this->ns[$type_namespace]; if ($p && !empty($this->complexTypes[$p][$type])) { if ($arrayType = $this->complexTypes[$p][$type]['arrayType']) { $type = 'Array'; } elseif ($this->complexTypes[$p][$type]['order'] == 'sequence' && array_key_exists('elements', $this->complexTypes[$p][$type])) { reset($this->complexTypes[$p][$type]['elements']); // assume an array if (count($this->complexTypes[$p][$type]['elements']) == 1) { $arg = current($this->complexTypes[$p][$type]['elements']); $arrayType = $arg['type']; $type = 'Array'; } else { foreach ($this->complexTypes[$p][$type]['elements'] as $element) { if ($element['name'] == $type) { $arrayType = $element['type']; $type = $element['type']; } } } } else { $type = 'Struct'; } return array($type, $arrayType, $type_namespace, null); } } return array(null, null, null, null); } /** * Recurse through the WSDL structure looking for the innermost array type * of multi-dimensional arrays. * * Takes a namespace prefix and a type, which can be in the form 'type' or * 'type[]', and returns the full namespace URI, the type of the most * deeply nested array type found, and the number of levels of nesting. * * @access private * @return mixed array or nothing */ function _getDeepestArrayType($nsPrefix, $arrayType) { static $trail = array(); - $arrayType = ereg_replace('\[\]$', '', $arrayType); + $arrayType = preg_replace('/\[\]$/', '', $arrayType); // Protect against circular references XXX We really need to remove // trail from this altogether (it's very inefficient and in the wrong // place!) and put circular reference checking in when the WSDL info // is generated in the first place if (array_search($nsPrefix . ':' . $arrayType, $trail)) { return array(null, null, -count($trail)); } if (array_key_exists($nsPrefix, $this->complexTypes) && array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) && array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) { $trail[] = $nsPrefix . ':' . $arrayType; $result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'], $this->complexTypes[$nsPrefix][$arrayType]['arrayType']); return array($result[0], $result[1], $result[2] + 1); } return array($this->namespaces[$nsPrefix], $arrayType, 0); } } class SOAP_WSDL_Cache extends SOAP_Base { /** * Use WSDL cache? * * @var boolean */ var $_cacheUse; /** * WSDL cache directory. * * @var string */ var $_cacheDir; /** * Cache maximum lifetime (in seconds) * * @var integer */ var $_cacheMaxAge; /** * Constructor. * * @param boolean $cashUse Use caching? * @param integer $cacheMaxAge Cache maximum lifetime (in seconds) */ function SOAP_WSDL_Cache($cacheUse = false, $cacheMaxAge = WSDL_CACHE_MAX_AGE, $cacheDir = null) { parent::SOAP_Base('WSDLCACHE'); $this->_cacheUse = $cacheUse; $this->_cacheDir = $cacheDir; $this->_cacheMaxAge = $cacheMaxAge; } /** * Returns the path to the cache and creates it, if it doesn't exist. * * @private * * @return string The directory to use for the cache. */ function _cacheDir() { if (!empty($this->_cacheDir)) { $dir = $this->_cacheDir; } else { $dir = getenv('WSDLCACHE'); if (empty($dir)) { $dir = './wsdlcache'; } } @mkdir($dir, 0700); return $dir; } /** * Retrieves a file from cache if it exists, otherwise retreive from net, * add to cache, and return from cache. * * @param string URL to WSDL * @param array proxy parameters * @param int expected MD5 of WSDL URL * @access public * @return string data */ function get($wsdl_fname, $proxy_params = array(), $cache = 0) { $cachename = $md5_wsdl = $file_data = ''; if ($this->_cacheUse) { // Try to retrieve WSDL from cache $cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl'; if (file_exists($cachename)) { $wf = fopen($cachename, 'rb'); if ($wf) { // Reading cached file $file_data = fread($wf, filesize($cachename)); $md5_wsdl = md5($file_data); fclose($wf); } if ($cache) { if ($cache != $md5_wsdl) { return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname); } } else { $fi = stat($cachename); $cache_mtime = $fi[8]; //print cache_mtime, time() if ($cache_mtime + $this->_cacheMaxAge < time()) { // expired $md5_wsdl = ''; // refetch } } } } if (!$md5_wsdl) { // Not cached or not using cache. Retrieve WSDL from URL // is it a local file? // this section should be replace by curl at some point if (!preg_match('/^(https?|file):\/\//', $wsdl_fname)) { if (!file_exists($wsdl_fname)) { return $this->_raiseSoapFault("Unable to read local WSDL $wsdl_fname", $wsdl_fname); } $file_data = file_get_contents($wsdl_fname); } else { $uri = explode('?', $wsdl_fname); $rq =new HTTP_Request($uri[0], $proxy_params); // the user agent HTTP_Request uses fouls things up if (isset($uri[1])) { $rq->addRawQueryString($uri[1]); } if (isset($proxy_params['proxy_host']) && isset($proxy_params['proxy_port']) && isset($proxy_params['proxy_user']) && isset($proxy_params['proxy_pass'])) { $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port'], $proxy_params['proxy_user'], $proxy_params['proxy_pass']); } elseif (isset($proxy_params['proxy_host']) && isset($proxy_params['proxy_port'])) { $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port']); } $result = $rq->sendRequest(); if (PEAR::isError($result)) { return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname); } $file_data = $rq->getResponseBody(); if (!$file_data) { return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname); } } $md5_wsdl = md5($file_data); if ($this->_cacheUse) { $fp = fopen($cachename, "wb"); fwrite($fp, $file_data); fclose($fp); } } if ($this->_cacheUse && $cache && $cache != $md5_wsdl) { return $this->_raiseSoapFault("WSDL Checksum error!", $wsdl_fname); } return $file_data; } } class SOAP_WSDL_Parser extends SOAP_Base { /** * Define internal arrays of bindings, ports, operations, * messages, etc. */ var $currentMessage; var $currentOperation; var $currentPortType; var $currentBinding; var $currentPort; /** * Parser vars. */ var $cache; var $tns = null; var $soapns = array('soap'); var $uri = ''; var $wsdl = null; var $status = ''; var $element_stack = array(); var $parentElement = ''; var $schema = ''; var $schemaStatus = ''; var $schema_stack = array(); var $currentComplexType; var $schema_element_stack = array(); var $currentElement; /** * Constructor. */ function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false) { parent::SOAP_Base('WSDLPARSER'); - $this->cache =new SOAP_WSDL_Cache($wsdl->cacheUse, + $this->cache = new SOAP_WSDL_Cache($wsdl->cacheUse, $wsdl->cacheMaxAge, $wsdl->cacheDir); $this->uri = $uri; $this->wsdl = &$wsdl; $this->docs = $docs; $this->parse($uri); } function parse($uri) { // Check whether content has been read. $fd = $this->cache->get($uri, $this->wsdl->proxy); if (PEAR::isError($fd)) { return $this->_raiseSoapFault($fd); } // Create an XML parser. $parser = xml_parser_create(); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_set_object($parser, $this); xml_set_element_handler($parser, 'startElement', 'endElement'); if ($this->docs) { xml_set_character_data_handler($parser, 'characterData'); } if (!xml_parse($parser, $fd, true)) { $detail = sprintf('XML error on line %d: %s', xml_get_current_line_number($parser), xml_error_string(xml_get_error_code($parser))); return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail"); } xml_parser_free($parser); return true; } /** * start-element handler */ function startElement($parser, $name, $attrs) { // Get element prefix. $qname = new QName($name); if ($qname->ns) { $ns = $qname->ns; if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) { $name = $qname->name; } } $this->currentTag = $qname->name; $this->parentElement = ''; $stack_size = count($this->element_stack); if ($stack_size) { $this->parentElement = $this->element_stack[$stack_size - 1]; } $this->element_stack[] = $this->currentTag; // Find status, register data. switch ($this->status) { case 'types': // sect 2.2 wsdl:types // children: xsd:schema $parent_tag = ''; $stack_size = count($this->schema_stack); if ($stack_size) { $parent_tag = $this->schema_stack[$stack_size - 1]; } switch ($qname->name) { case 'schema': // No parent should be in the stack. if (!$parent_tag || $parent_tag == 'types') { if (array_key_exists('targetNamespace', $attrs)) { $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']); } else { $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns); } $this->wsdl->complexTypes[$this->schema] = array(); $this->wsdl->elements[$this->schema] = array(); } break; case 'complexType': if ($parent_tag == 'schema') { $this->currentComplexType = $attrs['name']; if (!isset($attrs['namespace'])) { $attrs['namespace'] = $this->schema; } $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs; if (array_key_exists('base', $attrs)) { $qn = new QName($attrs['base']); $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name; $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns; } else { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; } $this->schemaStatus = 'complexType'; } else { $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true; } break; case 'element': if (isset($attrs['type'])) { $qn = new QName($attrs['type']); $attrs['type'] = $qn->name; if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) { $attrs['namespace'] = $qn->ns; } } $parentElement = ''; $stack_size = count($this->schema_element_stack); if ($stack_size > 0) { $parentElement = $this->schema_element_stack[$stack_size - 1]; } if (isset($attrs['ref'])) { $qn = new QName($attrs['ref']); $this->currentElement = $qn->name; } else { $this->currentElement = $attrs['name']; } $this->schema_element_stack[] = $this->currentElement; if (!isset($attrs['namespace'])) { $attrs['namespace'] = $this->schema; } if ($parent_tag == 'schema') { $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs; $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false; $this->schemaStatus = 'element'; } elseif ($this->currentComplexType) { // we're inside a complexType if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence') && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null; } $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs; } else { $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs; } break; case 'complexContent': case 'simpleContent': break; case 'extension': case 'restriction': if ($this->schemaStatus == 'complexType') { if (!empty($attrs['base'])) { $qn = new QName($attrs['base']); $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name; // Types that extend from other types aren't // *of* those types. Reflect this by denoting // which type they extend. I'm leaving the // 'type' setting here since I'm not sure what // removing it might break at the moment. if ($qname->name == 'extension') { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name; } } else { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; } } break; case 'sequence': if ($this->schemaStatus == 'complexType') { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name; if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; } } break; case 'all': $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name; if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; } break; case 'choice': $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name; if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; } case 'attribute': if ($this->schemaStatus == 'complexType') { if (isset($attrs['name'])) { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs; } else { if (isset($attrs['ref'])) { $q = new QName($attrs['ref']); foreach ($attrs as $k => $v) { if ($k != 'ref' && strstr($k, $q->name)) { $vq = new QName($v); if ($q->name == 'arrayType') { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo; $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->ns; } else { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name; } } } } } } break; } $this->schema_stack[] = $qname->name; break; case 'message': // sect 2.3 wsdl:message child wsdl:part switch ($qname->name) { case 'part': $qn = null; if (isset($attrs['type'])) { $qn = new QName($attrs['type']); } elseif (isset($attrs['element'])) { $qn = new QName($attrs['element']); } if ($qn) { $attrs['type'] = $qn->name; $attrs['namespace'] = $qn->ns; } $this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs; // error in wsdl case 'documentation': break; default: break; } break; case 'portType': // sect 2.4 switch ($qname->name) { case 'operation': // attributes: name // children: wsdl:input wsdl:output wsdl:fault $this->currentOperation = $attrs['name']; $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs; break; case 'input': case 'output': case 'fault': // wsdl:input wsdl:output wsdl:fault // attributes: name message parameterOrder(optional) if ($this->currentOperation) { if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) { $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name], $attrs); } else { $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs; } if (array_key_exists('message', $attrs)) { $qn = new QName($attrs['message']); $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name; $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns; } } break; case 'documentation': break; default: break; } break; case 'binding': $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; switch ($ns) { case SCHEMA_SOAP: case SCHEMA_SOAP12: // this deals with wsdl section 3 soap binding switch ($qname->name) { case 'binding': // sect 3.3 // soap:binding, attributes: transport(required), style(optional, default = document) // if style is missing, it is assumed to be 'document' if (!isset($attrs['style'])) { $attrs['style'] = 'document'; } $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs); break; case 'operation': // sect 3.4 // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style) if (!isset($attrs['style'])) { $attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style']; } if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) { $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = array_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation], $attrs); } else { $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs; } break; case 'body': // sect 3.5 // soap:body attributes: // part - optional. listed parts must appear in body, missing means all parts appear in body // use - required. encoded|literal // encodingStyle - optional. space seperated list of encodings (uri's) $this->wsdl->bindings[$this->currentBinding] ['operations'][$this->currentOperation][$this->opStatus] = $attrs; break; case 'fault': // sect 3.6 // soap:fault attributes: name use encodingStyle namespace $this->wsdl->bindings[$this->currentBinding] ['operations'][$this->currentOperation][$this->opStatus] = $attrs; break; case 'header': // sect 3.7 // soap:header attributes: message part use encodingStyle namespace $this->wsdl->bindings[$this->currentBinding] ['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs; break; case 'headerfault': // sect 3.7 // soap:header attributes: message part use encodingStyle namespace $header = count($this->wsdl->bindings[$this->currentBinding] ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1; $this->wsdl->bindings[$this->currentBinding] ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs; break; case 'documentation': break; default: // error! not a valid element inside binding break; } break; case SCHEMA_WSDL: // XXX verify correct namespace // for now, default is the 'wsdl' namespace // other possible namespaces include smtp, http, etc. for alternate bindings switch ($qname->name) { case 'operation': // sect 2.5 // wsdl:operation attributes: name $this->currentOperation = $attrs['name']; break; case 'output': case 'input': case 'fault': // sect 2.5 // wsdl:input attributes: name $this->opStatus = $qname->name; break; case 'documentation': break; default: break; } break; case SCHEMA_WSDL_HTTP: switch ($qname->name) { case 'binding': // sect 4.4 // http:binding attributes: verb // parent: wsdl:binding $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs); break; case 'operation': // sect 4.5 // http:operation attributes: location // parent: wsdl:operation $this->wsdl->bindings[$this->currentBinding]['operations'] [$this->currentOperation] = $attrs; break; case 'urlEncoded': // sect 4.6 // http:urlEncoded attributes: location // parent: wsdl:input wsdl:output etc. $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus] [$this->currentOperation]['uri'] = 'urlEncoded'; break; case 'urlReplacement': // sect 4.7 // http:urlReplacement attributes: location // parent: wsdl:input wsdl:output etc. $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus] [$this->currentOperation]['uri'] = 'urlReplacement'; break; case 'documentation': break; default: // error break; } case SCHEMA_MIME: // sect 5 // all mime parts are children of wsdl:input, wsdl:output, etc. // unsuported as of yet switch ($qname->name) { case 'content': // sect 5.3 mime:content // // part attribute only required if content is child of multipart related, // it contains the name of the part // type attribute contains the mime type case 'multipartRelated': // sect 5.4 mime:multipartRelated case 'part': case 'mimeXml': // sect 5.6 mime:mimeXml // // case 'documentation': break; default: // error break; } case SCHEMA_DIME: // DIME is defined in: // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm // all DIME parts are children of wsdl:input, wsdl:output, etc. // unsuported as of yet switch ($qname->name) { case 'message': // sect 4.1 dime:message // appears in binding section $this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs; break; default: break; } default: break; } break; case 'service': $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; switch ($qname->name) { case 'port': // sect 2.6 wsdl:port attributes: name binding $this->currentPort = $attrs['name']; $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs; // XXX hack to deal with binding namespaces $qn = new QName($attrs['binding']); $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name; $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns; break; case 'address': $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs; // what TYPE of port is it? SOAP or HTTP? $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; switch ($ns) { case SCHEMA_WSDL_HTTP: $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http'; break; case SCHEMA_SOAP: $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap'; break; default: // Shouldn't happen, we'll assume SOAP. $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap'; } break; case 'documentation': break; default: break; } } // Top level elements found under wsdl:definitions. switch ($qname->name) { case 'import': // sect 2.1.1 wsdl:import attributes: namespace location if ((isset($attrs['location']) || isset($attrs['schemaLocation'])) && !isset($this->wsdl->imports[$attrs['namespace']])) { $uri = isset($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation']; $location = @parse_url($uri); if (!isset($location['scheme'])) { $base = @parse_url($this->uri); $uri = $this->mergeUrl($base, $uri); } $this->wsdl->imports[$attrs['namespace']] = $attrs; $import_parser_class = get_class($this); $import_parser =new $import_parser_class($uri, $this->wsdl, $this->docs); if ($import_parser->fault) { unset($this->wsdl->imports[$attrs['namespace']]); return false; } $this->currentImport = $attrs['namespace']; } // Continue on to the 'types' case - lack of break; is // intentional. case 'types': // sect 2.2 wsdl:types $this->status = 'types'; break; case 'schema': // We can hit this at the top level if we've been asked to // import an XSD file. if (!empty($attrs['targetNamespace'])) { $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']); } else { $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns); } $this->wsdl->complexTypes[$this->schema] = array(); $this->wsdl->elements[$this->schema] = array(); $this->schema_stack[] = $qname->name; $this->status = 'types'; break; case 'message': // sect 2.3 wsdl:message attributes: name children:wsdl:part $this->status = 'message'; if (isset($attrs['name'])) { $this->currentMessage = $attrs['name']; $this->wsdl->messages[$this->currentMessage] = array(); } break; case 'portType': // sect 2.4 wsdl:portType // attributes: name // children: wsdl:operation $this->status = 'portType'; $this->currentPortType = $attrs['name']; $this->wsdl->portTypes[$this->currentPortType] = array(); break; case 'binding': // sect 2.5 wsdl:binding attributes: name type // children: wsdl:operation soap:binding http:binding if ($qname->ns && $qname->ns != $this->tns) { break; } $this->status = 'binding'; $this->currentBinding = $attrs['name']; $qn = new QName($attrs['type']); $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name; $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns; break; case 'service': // sect 2.7 wsdl:service attributes: name children: ports $this->currentService = $attrs['name']; $this->wsdl->services[$this->currentService]['ports'] = array(); $this->status = 'service'; break; case 'definitions': // sec 2.1 wsdl:definitions // attributes: name targetNamespace xmlns:* // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service $this->wsdl->definition = $attrs; foreach ($attrs as $key => $value) { if (strstr($key, 'xmlns:') !== false) { $qn = new QName($key); // XXX need to refactor ns handling. $this->wsdl->namespaces[$qn->name] = $value; $this->wsdl->ns[$value] = $qn->name; if ($key == 'targetNamespace' || strcasecmp($value,SOAP_SCHEMA) == 0) { $this->soapns[] = $qn->name; } else { if (in_array($value, $this->_XMLSchema)) { $this->wsdl->xsd = $value; } } } } if (isset($ns) && $ns) { $namespace = 'xmlns:' . $ns; if (!$this->wsdl->definition[$namespace]) { return $this->_raiseSoapFault("parse error, no namespace for $namespace", $this->uri); } $this->tns = $ns; } break; } } /** * end-element handler. */ function endElement($parser, $name) { $stacksize = count($this->element_stack); if ($stacksize) { if ($this->element_stack[$stacksize - 1] == 'definitions') { $this->status = ''; } array_pop($this->element_stack); } if (stristr($name, 'schema')) { array_pop($this->schema_stack); $this->schema = ''; } if ($this->schema) { array_pop($this->schema_stack); if (count($this->schema_stack) <= 1) { /* Correct the type for sequences with multiple * elements. */ if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type']) && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array' && array_key_exists('elements', $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]) && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; } } if (stristr($name, 'complexType')) { $this->currentComplexType = ''; if (count($this->schema_element_stack)) { $this->currentElement = array_pop($this->schema_element_stack); } else { $this->currentElement = ''; } } elseif (stristr($name, 'element')) { if (count($this->schema_element_stack)) { $this->currentElement = array_pop($this->schema_element_stack); } else { $this->currentElement = ''; } } } } /** * Element content handler. */ function characterData($parser, $data) { // Store the documentation in the WSDL file. if ($this->currentTag == 'documentation') { $data = trim(preg_replace('/\s+/', ' ', $data)); if (!strlen($data)) { return; } switch ($this->status) { case 'service': $ptr =& $this->wsdl->services[$this->currentService]; break; case 'portType': $ptr =& $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation]; break; case 'binding': $ptr =& $this->wsdl->bindings[$this->currentBinding]; break; case 'message': $ptr =& $this->wsdl->messages[$this->currentMessage]; break; case 'operation': break; case 'types': if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) { if ($this->currentElement) { $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement]; } else { $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]; } } break; } if (isset($ptr)) { if (!isset($ptr['documentation'])) { $ptr['documentation'] = ''; } else { $ptr['documentation'] .= ' '; } $ptr['documentation'] .= $data; } } } /** * $parsed is an array returned by parse_url(). * * @access private */ function mergeUrl($parsed, $path) { if (!is_array($parsed)) { return false; } $uri = ''; if (!empty($parsed['scheme'])) { $sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'); $uri = $parsed['scheme'] . $sep; } if (isset($parsed['pass'])) { $uri .= "$parsed[user]:$parsed[pass]@"; } elseif (isset($parsed['user'])) { $uri .= "$parsed[user]@"; } if (isset($parsed['host'])) { $uri .= $parsed['host']; } if (isset($parsed['port'])) { $uri .= ":$parsed[port]"; } if ($path[0] != '/' && isset($parsed['path'])) { if ($parsed['path'][strlen($parsed['path']) - 1] != '/') { $path = dirname($parsed['path']) . '/' . $path; } else { $path = $parsed['path'] . $path; } $path = $this->_normalize($path); } $sep = $path[0] == '/' ? '' : '/'; $uri .= $sep . $path; return $uri; } function _normalize($path_str) { $pwd = ''; $strArr = preg_split('/(\/)/', $path_str, -1, PREG_SPLIT_NO_EMPTY); $pwdArr = ''; $j = 0; for ($i = 0; $i < count($strArr); $i++) { if ($strArr[$i] != ' ..') { if ($strArr[$i] != ' .') { $pwdArr[$j] = $strArr[$i]; $j++; } } else { array_pop($pwdArr); $j--; } } $pStr = implode('/', $pwdArr); $pwd = (strlen($pStr) > 0) ? ('/' . $pStr) : '/'; return $pwd; } } /** * Parses the types and methods used in web service objects into the internal * data structures used by SOAP_WSDL. * * Assumes the SOAP_WSDL class is unpopulated to start with. * * @author Chris Coe */ class SOAP_WSDL_ObjectParser extends SOAP_Base { /** * Target namespace for the WSDL document will have the following * prefix. */ var $tnsPrefix = 'tns'; /** * Reference to the SOAP_WSDL object to populate. */ var $wsdl = null; /** * Constructor. * * @param object|array $objects Reference to the object or array of * objects to parse. * @param SOAP_WSDL $wsdl Reference to the SOAP_WSDL object to * populate. * @param string $targetNamespace The target namespace of schema types * etc. * @param string $service_name Name of the WSDL . * @param string $service_desc Optional description of the WSDL * . */ function SOAP_WSDL_ObjectParser(&$objects, &$wsdl, $targetNamespace, $service_name, $service_desc = '') { parent::SOAP_Base('WSDLOBJECTPARSER'); $this->wsdl = &$wsdl; // Set up the SOAP_WSDL object $this->_initialise($service_name); // Parse each web service object $wsdl_ref = (is_array($objects)? $objects : array(&$objects)); foreach ($wsdl_ref as $ref_item) { if (!is_object($ref_item)) { $this->_raiseSoapFault('Invalid web service object passed to object parser'); continue; } if (!$this->_parse($ref_item, $targetNamespace, $service_name)) { break; } } // Build bindings from abstract data. if ($this->fault == null) { $this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc); } } /** * Initialise the SOAP_WSDL tree (destructive). * * If the object has already been initialised, the only effect * will be to change the tns namespace to the new service name. * * @param $service_name Name of the WSDL * @access private */ function _initialise($service_name) { // Set up the basic namespaces that all WSDL definitions use. $this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL; // WSDL language $this->wsdl->namespaces['soap'] = SCHEMA_SOAP; // WSDL SOAP bindings $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema $this->wsdl->namespaces['SOAP-ENC'] = array_search('SOAP-ENC', $this->_namespaces); // SOAP types // XXX Refactor $namespace/$ns for Shane :-) unset($this->wsdl->ns['urn:' . $service_name]); $this->wsdl->ns += array_flip($this->wsdl->namespaces); // Imports are not implemented in WSDL generation from classes. // *** *** } /** * Parser - takes a single object to add to tree (non-destructive). * * @access private * * @param object $object Reference to the object to parse. * @param string $schemaNamespace * @param string $service_name Name of the WSDL . */ function _parse(&$object, $schemaNamespace, $service_name) { // Create namespace prefix for the schema list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}'); // Parse all the types defined by the object in whatever // schema language we are using (currently __typedef arrays) // *** *** foreach ($object->__typedef as $typeName => $typeValue) { // Get/create namespace definition list($nsPrefix, $typeName) = $this->_getTypeNs($typeName); // Create type definition $this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName); $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName]; // According to Dmitri's documentation, __typedef comes in two // flavors: // Array = array(array("item" => "value")) // Struct = array("item1" => "value1", "item2" => "value2", ...) if (is_array($typeValue)) { if (is_array(current($typeValue)) && count($typeValue) == 1 && count(current($typeValue)) == 1) { // It's an array $thisType['type'] = 'Array'; $nsType = current(current($typeValue)); list($nsPrefix, $typeName) = $this->_getTypeNs($nsType); $thisType['namespace'] = $nsPrefix; $thisType['arrayType'] = $typeName . '[]'; } elseif (!is_array(current($typeValue))) { // It's a struct $thisType['type'] = 'Struct'; $thisType['order'] = 'all'; $thisType['namespace'] = $nsPrefix; $thisType['elements'] = array(); foreach ($typeValue as $elementName => $elementType) { list($nsPrefix, $typeName) = $this->_getTypeNs($elementType); $thisType['elements'][$elementName]['name'] = $elementName; $thisType['elements'][$elementName]['type'] = $typeName; $thisType['elements'][$elementName]['namespace'] = $nsPrefix; } } else { // It's erroneous return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object)); } } else { // It's erroneous return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object)); } } // Create an empty element array with the target namespace // prefix, to match the results of WSDL parsing. $this->wsdl->elements[$schPrefix] = array(); // Populate tree with message information // *** *** foreach ($object->__dispatch_map as $operationName => $messages) { foreach ($messages as $messageType => $messageParts) { unset($thisMessage); switch ($messageType) { case 'in': $this->wsdl->messages[$operationName . 'Request'] = array(); $thisMessage =& $this->wsdl->messages[$operationName . 'Request']; break; case 'out': $this->wsdl->messages[$operationName . 'Response'] = array(); $thisMessage =& $this->wsdl->messages[$operationName . 'Response']; break; case 'alias': // Do nothing break; default: // Error condition break; } if (isset($thisMessage)) { foreach ($messageParts as $partName => $partType) { list ($nsPrefix, $typeName) = $this->_getTypeNs($partType); $thisMessage[$partName] = array( 'name' => $partName, 'type' => $typeName, 'namespace' => $nsPrefix ); } } } } // Populate tree with portType information // XXX Current implementation only supports one portType that // encompasses all of the operations available. // *** *** if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) { $this->wsdl->portTypes[$service_name . 'Port'] = array(); } $thisPortType =& $this->wsdl->portTypes[$service_name . 'Port']; foreach ($object->__dispatch_map as $operationName => $messages) { $thisPortType[$operationName] = array('name' => $operationName); foreach ($messages as $messageType => $messageParts) { switch ($messageType) { case 'in': $thisPortType[$operationName]['input'] = array( 'message' => $operationName . 'Request', 'namespace' => $this->tnsPrefix); break; case 'out': $thisPortType[$operationName]['output'] = array( 'message' => $operationName . 'Response', 'namespace' => $this->tnsPrefix); break; } } } return true; } /** * Takes all the abstract WSDL data and builds concrete bindings and * services (destructive). * * @access private * @todo Current implementation discards $service_desc. * * @param string $schemaNamespace Namespace for types etc. * @param string $service_name Name of the WSDL . * @param string $service_desc Optional description of the WSDL * . */ function _generateBindingsAndServices($schemaNamespace, $service_name, $service_desc = '') { // Populate tree with bindings information // XXX Current implementation only supports one binding that // matches the single portType and all of its operations. // XXX Is this the correct use of $schemaNamespace here? // *** *** $this->wsdl->bindings[$service_name . 'Binding'] = array( 'type' => $service_name . 'Port', 'namespace' => $this->tnsPrefix, 'style' => 'rpc', 'transport' => SCHEMA_SOAP_HTTP, 'operations' => array()); $thisBinding =& $this->wsdl->bindings[$service_name . 'Binding']; foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData) { $thisBinding['operations'][$operationName] = array( 'soapAction' => $schemaNamespace . '#' . $operationName, 'style' => $thisBinding['style']); foreach (array('input', 'output') as $messageType) if (isset($operationData[$messageType])) { $thisBinding['operations'][$operationName][$messageType] = array( 'use' => 'encoded', 'namespace' => $schemaNamespace, 'encodingStyle' => SOAP_SCHEMA_ENCODING); } } // Populate tree with service information // XXX Current implementation supports one service which groups // all of the ports together, one port per binding // *** *** $this->wsdl->services[$service_name . 'Service'] = array('ports' => array()); $thisService =& $this->wsdl->services[$service_name . 'Service']['ports']; $https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) || getenv('SSL_PROTOCOL_VERSION'); foreach ($this->wsdl->bindings as $bindingName => $bindingData) { $thisService[$bindingData['type']] = array( 'name' => $bindingData['type'], 'binding' => $bindingName, 'namespace' => $this->tnsPrefix, 'address' => array('location' => ($https ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')), 'type' => 'soap'); } // Set service $this->wsdl->set_service($service_name . 'Service'); $this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix]; // Create WSDL definition // *** *** $this->wsdl->definition = array( 'name' => $service_name, 'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix], 'xmlns' => SCHEMA_WSDL); foreach ($this->wsdl->namespaces as $nsPrefix => $namespace) { $this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace; } } /** * This function is adapted from Dmitri V's implementation of * DISCO/WSDL generation. It separates namespace from type name in * a __typedef key and creates a new namespace entry in the WSDL * structure if the namespace has not been used before. The * namespace prefix and type name are returned. If no namespace is * specified, xsd is assumed. * * We will not need this function anymore once __typedef is * eliminated. */ function _getTypeNs($type) { preg_match_all('/\{(.*)\}/sm', $type, $m); if (!empty($m[1][0])) { if (!isset($this->wsdl->ns[$m[1][0]])) { $ns_pref = 'ns' . count($this->wsdl->namespaces); $this->wsdl->ns[$m[1][0]] = $ns_pref; $this->wsdl->namespaces[$ns_pref] = $m[1][0]; } $typens = $this->wsdl->ns[$m[1][0]]; $type = str_replace($m[0][0], '', $type); } else { $typens = 'xsd'; } return array($typens, $type); } } diff --git a/phplib/of_text.inc b/phplib/of_text.inc index e984601..b4dc238 100644 --- a/phplib/of_text.inc +++ b/phplib/of_text.inc @@ -1,124 +1,124 @@ setup_element($a); if ($a["type"]=="password") $this->pass=1; } function self_get($val,$which, &$count) { $str = ""; if (is_array($this->value)) $v = htmlspecialchars($this->value[$which]); else $v = htmlspecialchars($this->value); $n = $this->name . ($this->multiple ? "[]" : ""); $str .= "pass)? " type='password'" : " type='text'"; //$str .= " class=$class"; if ($this->maxlength) $str .= " maxlength='$this->maxlength'"; if ($this->size) $str .= " size='$this->size'"; if ($this->extrahtml) $str .= " $this->extrahtml"; $str .= "autocomplete='off'>"; $count = 1; return $str; } function self_get_frozen($val,$which, &$count) { $str = ""; if (is_array($this->value)) $v = $this->value[$which]; else $v = $this->value; $n = $this->name . ($this->multiple ? "[]" : ""); $str .= "\n"; $str .= "$v\n"; $count = 1; return $str; } function self_get_js($ndx_array) { $str = ""; reset($ndx_array); while (list($k,$n) = each($ndx_array)) { if ($this->length_e) { $str .= "if (f.elements[${n}].value.length < $this->minlength) {\n"; $str .= " alert(\"Too short!\");\n"; $str .= " f.elements[${n}].focus();\n"; $str .= " return(false);\n}\n"; } if ($this->valid_e) { $flags = ($this->icase ? "gi" : "g"); $str .= "if (window.RegExp) {\n"; $str .= " var reg = new RegExp(\"$this->valid_regex\",\"$flags\");\n"; $str .= " if (!reg.test(f.elements[${n}].value)) {\n"; $str .= " alert(\"$this->valid_e\");\n"; $str .= " f.elements[${n}].focus();\n"; $str .= " return(false);\n"; $str .= " }\n}\n"; } } - + return $str; } function self_validate($val) { if (!is_array($val)) $val = array($val); reset($val); while (list($k,$v) = each($val)) { if ($this->length_e && (strlen($v) < $this->minlength)) if ($this->valid_minlength_error) { return $this->valid_minlength_error; } else { return $this->valid_e; } - if ($this->valid_e && (($this->icase && - !eregi($this->valid_regex,$v)) || + if ($this->valid_e && (($this->icase && + !preg_match("/$this->valid_regex/i",$v)) || (!$this->icase && - !ereg($this->valid_regex,$v)))) + !preg_match("/$this->valid_regex/",$v)))) return $this->valid_e; // new added by adigeo 2001-05-28 - if ($this->valid_e2_error && (($this->icase && - !eregi($this->valid_e2,$v)) || + if ($this->valid_e2_error && (($this->icase && + !preg_match("/$this->valid_e2/i",$v)) || (!$this->icase && - !ereg($this->valid_e2,$v)))) + !preg_match("/$this->valid_e2/",$v)))) return $this->valid_e2_error; } return false; - } + } } // end TEXT ?> diff --git a/phplib/query_sql.inc b/phplib/query_sql.inc index 34d9fa2..4eeb4c7 100644 --- a/phplib/query_sql.inc +++ b/phplib/query_sql.inc @@ -1,742 +1,742 @@ * I took many ideas from his SQL.inc, thanks! :-) * The idea is of this class is based in November 1997, * it was a collection of functions for PHP/FI and mSQL. * * $Id: query_sql.inc,v 1.1.1.1 2004-04-29 10:18:10 adigeo Exp $ * */ /* The Query-class is an enhancement to the db_*-classes. Currently It supports mySQL an Oracle but it is easy expandable. See down. It always needs the class DB_Sql! Query is fully upward compatible with DB_Sql. Example: OLD: NEW: require("db_mysql.inc"); require("db_mysql.inc"); class hugobla extends DB_sql {} require("query_sql.inc"); $db = new hugobla; class hugobla extends Query {} $db = new hugobla; It just provides a number of new functions. The Query-class is inteded to help you with creating selects, inserts, updates, where-clauses etc. Not just *simple* selects, but longer ones. It is indeed a great help for tables with more than 10-20 columns. But it can also be used for simple and small selects. The inbuilt checks help you programming saver. Here is an example: file: insert.php3 ------------------ query($db->insert_plain_Clause("mytable",$db->capture_vars("mytable"),ARRAY())); echo "Values inserted"; ?> file: insert2.php3 ------------------- query($db->insert_plain_Clause("mytable",$db->capture_vars("mytable"), ARRAY(myindex=>'NULL',mytime='func'))); echo "Values inserted: "$db->last_insert_id(); ?> This example is nice, cause you see how easy it can be used. :-) The best thing is, that you don't have to care, if a field is a string or a number. The values are automatically converted into the right form. The type of the vars are read from the table. Stringvalues are encapsulated with ' (configurable) and escaped (the code for this is currently not good - we are assuming, that PHP is configured not to encapsulate strings), int- and real-values are casted. It can handle "NULL"-values, function-statements or other values for insertion. You will make less errors. mySQL and most other DB's accept a a short form of insert-clause (INSERT INTO bla VALUES (...)). The Query-class will always make the longer form (INSERT INTO BLA (...) VALUES (...)). This makes it possible to use ALTER TABLE-commands without changing the program! E.g. changing a field in a table from NUMBER to VARCHAR(10) is fully encapsulated with this class. The class supports currently only mysql and oracle. I think the differences between the DBs are encapsulated enough in the db_* classes, so it is possible to handle the remaining small differences inside this class (this affects mainly the function sql2phptype() ) and it could be easiely extended (asuming, that the metadata()-function of the db_*-class works correctly). In this case it is important, that the $type-variable in the db_*.inc-class is correctly set. TODO-list: - A method to create querys like the LIMIT-clause in mySQL. For Oracle this works: select linenum, foo, bar from (select rownum as linenum, foo, bar from (select foo,bar from chaos order by bar) ) where linenum between 11 and 20; - cleaner escaping, handling of \ and NUL (current code is bullshit) Some ideas? - Little Alta-Vista: add functions to create a where clause from a search string with rules to handle searching for more than one word. half automatic generating search patterns into a where-clause simple search engine support, simple support for semi full-text-search - automatic configurable manipulation of values, eg. triming of strings (delete whitespace at begin and end) also : TOUPPER, TOLOWER etc - SELECT-Clause (GROUP BY, HAVING, JOIN...) - make new functions insert_Clause() etc. which inserts only the fields they got from your call (the current will do "plain" insert) - where_Clause() - creating WHERE for select, update, exists etc. - serv all queries directly into db, return just the handle (hm, how to deal with the DB-handle?) - Return a 2-dimensional (Table-compatible) field from select (not so important) - The sql2phptype() can be used to help creating automatic input forms for a table DEPENDING: - db_mysql: new function metatabledata(), which returns the table-info from current selected table (will return multiple table-columns with a join) - db_mysql: perhaps the function sql2phptype() should be placed there? For developers of new db_*.inc: the function metadata() is very important for the correct work of this class. T */ class Query extends DB_Sql { ## DONT FORGET to set the variables from DB_Sql! See there! ## For debugging: if set to TRUE the Query is printed out, ## before executing or returning var $Q_Debug=false; ## set this to another value, if you want to hide it ## in your HTML-code ## example: var $Q_Debug_print="\n\n"; var $Q_Debug_print="
QDebug:\n%s\n
\n"; ## Set this to TRUE if you only want to test, which query ## will be created (ideal in combination with $Q_Debug) ## This depends only functions which will make changes var $No_Write=false; ## currently unused, this var is just an idea for later use. var $Backslash_N_is_NULL = false; ## Metacache: see funtcion metadata_buffered() var $meta_cache_off = false; ## This is the char, which will be replaced by \char. ## PHP3 seems to be NOT binary-safe, so not quoting ## for \0 (some ideas?) ## we use ereg_replace to do the replacements. ## with PHP3.0.6 you should replace this with str_replace()! ## Quoting = 1 -> normal quoting using AddSlashes ## 2 -> Replace \' to '' - needed eg. for sybase or oracle var $Quoting=1; var $Quotechar="'"; var $StrLengTrunc = false; var $StrLengWarn = false; ########################### ## _QDebug function _QDebug ($str) { if ($this->Q_Debug) { printf($this->Q_Debug_print,$str); } } ########################### ## Set DB-Classname ## This is only a 3rd posibility for setting the classname ## function set_db_class ($db_class) { $this->Database=$db_class; } ########################### ## This function gets a datatype from the DB and returns an ## equivalent datatype for PHP ## ## It returns also a subtype for a string ## function sql2phptype ($type,$format='') { ## $this->type is currently either "mysql" or "oracle" switch ($this->type) { case "mysql": switch ($type) { case "var string": case "string" : case "char" : return(Array("string","")); break; case "timestamp" : case "datetime" : case "date" : case "time" : return(Array("string","date")); break; case "blob" : return(Array("string","blob")); break; case "real" : return(Array("double","")); break; case "long" : default : return(Array("int","")); break; } break; case "oracle": switch ($type) { case "VARCHAR2" : case "VARCHAR" : case "CHAR" : return(Array("string","")); break; case "DATE" : return(Array("string","date")); break; case "BLOB" : case "CLOB" : case "BFILE" : case "RAW" : case "LONG" : case "LONG RAW" : return(Array("string","blob")); break; case "NUMBER" : if ($format) { return(Array("double","")); } else { return(Array("int","")); } break; default : $this->halt("sql2phptype(): Type is not a valid value: '$type'"); break; } break; default: $this->halt("sql2phptype(): DB-type is not a valid value: ".$this->type); break; } } ####################################### ## This function returns a PHP-variable depending ## on type. E.g. a string is returned as 'string' ## ## The parameters mean ## $val - the value ## There is a special case: If value is "NULL" and ## the type is not "string" or subtype is empty, then ## a value "NULL" is inserted. This let you just spare ## a little bit work with $special ## ## $meta - the meta information for this field (that's what ## is returned by metadata() from DB_sql-class, but just one ## single row, e.g. $meta[2], not hole $meta). ## ## $special - Overwrites the type of the var if set. Some special ## meanings: ## "NULL" means, that this value must be set to "NULL" ## "func" means, that $val should be untouched - ## e.g. to insert the value of a SQL-function ## [ INSERT INTO bla VALUES ( time=NOW() ) ] ## function convert ($val,$meta,$special="") { list($type,$subtype)=$this->sql2phptype($meta["type"],$meta["format"]); if (($val == "NULL" && ($type != "string" || $type[1] != "")) || $special == "NULL") { $type="NULL"; } else { if ($special) { $type=$special; if ($type!="func") { $val=$type; $type="func"; } } } switch ($type) { case "string" : $val=(string)$val; if ($this->Quoting) { $val=AddSlashes($val); } if ($this->Quoting==2) { $val=str_replace("\\'","''",$val); } if ($subtype!='date' && ( $this->StrLengTrunc || $this->StrLengWarn ) ) { if ( strlen($val) > $meta[len] ) { if ($this->StrLengWarn) { echo "
STRING TOO LONG: '$meta[name]'"; if ($this->StrLengTrunc) { echo ", TRUNCATING!"; } } if ($this->StrLengTrunc) { $val=substr($val,0,$meta[len]); } } } $val=$this->Quotechar . $val . $this->Quotechar; break; case "int" : $val=(int)$val; break; case "double" : $val=(double)$val; break; case "NULL" : $val="NULL"; break; case "func" : $val=(string)$val; break; default : echo "UNKNOWN TYPE: $type
"; } $this->_QDebug("Val: $meta[name] => $val
"); return(Array($val,$meta["name"])); } ## ## Function to generate a plain INSERT-Clause ## ("plain" means, that every field in the table will ## be set to a value, default is '' or 0 if nothing said ## in $special) ## ## $fields is an assoc. Array consisting out of ## table_name => value-pairs ## $special is an assoc. field which will commit special ## handling to convert() (See there) ## $check could be "strong" or "soft". ## "soft" won't tell you if there were to less ## or too much fields (good for debuging) ## ## returns the insert clause. It's on you to modify it ## and send it to your DB ## function insert_plain_Clause ($table,$fields,$special,$check="soft") { $meta=$this->metadata_buffered($table); for ($i=0; $i < $meta["num_fields"]; $i++) { $j=$meta[$i]["name"]; ## NOT IMPLEMENTED: SEARCHING FOR $fields[$i] list($val["val"][$i],$val["name"][$i])= $this->convert($fields[$j],$meta[$i],$special[$j]); } if (Count($fields)!=Count($val["name"]) && $check=="strong") { echo "WARNING: insert_plain_clause(): There are not the same number of". " fields as in table for INSERT
"; } $q=sprintf("INSERT INTO %s (%s) VALUES (%s)", $table,join($val["name"],","), join($val["val"],",")); $this->_QDebug($q); return($q); } # Replace, a special mySQL-function, same as INSERT function replace_plain_Clause ($table,$fields,$special,$check="soft") { $meta=$this->metadata_buffered($table); for ($i=0; $i < $meta["num_fields"]; $i++) { $j=$meta[$i]["name"]; ## NOT IMPLEMENTED: SEARCHING FOR $fields[$i] list($val["val"][$i],$val["name"][$i])= $this->convert($fields[$j],$meta[$i],$special[$j]); } if (Count($fields)!=Count($val["name"]) && $check=="strong") { echo "WARNING: replace_plain_Clause(): There are not the same number of". " fields as in table for INSERT
"; } $q=sprintf("REPLACE %s (%s) VALUES (%s)", $table,join($val["name"],","), join($val["val"],",")); $this->_QDebug($q); return($q); } ## ## This function is nearly the same, as insert_plain_Clause, ## The where parameter is new and should be generated by yourself ## The check parameter knows 3 values: strong, soft and weak ## weak enables you to sent a query without $where (enables you ## to update the hole table) ## function update_plain_Clause ($table,$fields,$special,$where,$check="soft") { $meta=$this->metadata_buffered($table); if (!$where && $check!="weak") { echo "ERROR: update_plain_Clause(): Parameter \$where is empty!
"; return(false); } for ($i=0; $i < $meta["num_fields"]; $i++) { $j=$meta[$i]["name"]; ## NOT IMPLEMENTED: SEARCHING FOR $fields[$i] list($val["val"][$i],$val["name"][$i])= $this->convert($fields[$j],$meta[$i],$special[$j]); #echo "V: ".$val["name"][$i]." : ". $val["val"][$i]." - ".$fields[$j]."
"; } if (Count($fields)!=Count($val["name"]) && $check=="strong") { echo "WARNING: update_plain_Clause(): There are not the same number of". " fields for INSERT
"; } for ($i=0 ; $i < Count ($val["name"]); $i++ ) { $s[]=$val["name"][$i]."=".$val["val"][$i]; } $q=sprintf("UPDATE %s SET %s",$table,join($s,",")); if ($where) { - if (!eregi("^[[:space:]]*WHERE",$where)) { + if (!preg_match("/^[[:space:]]*WHERE/i",$where)) { ## insert "WHERE" if not set $where="WHERE $where"; } $q.=" $where"; } $this->_QDebug($q); return($q); } ## ## This function is nearly the same, as insert_Clause, ## The where parameter is new and should be generated by yourself ## The check parameter knows 3 values: strong, soft and weak ## weak enables you to sent a query without $where (enables you ## to update the hole table) ## function update_Clause ($table,$fields,$special,$where,$check="soft") { $meta=$this->metadata_buffered($table); if (!$where && $check!="weak") { echo "ERROR: update_Clause(): Parameter \$where is empty!
"; return(false); } $i=0; for (reset($fields); list($key,$val)=each($fields); $i++) { if ( isset($meta[meta][$key]) ) { $j=$meta[meta][$key]; list($v["val"][$i],$v["name"][$i])= $this->convert($val,$meta[$j],$special[$key]); } } for ($i=0 ; $i < Count ($v["name"]); $i++ ) { $s[]=$v["name"][$i]."=".$v["val"][$i]; } if (Count($s)) { $q=sprintf("UPDATE %s SET %s",$table,join($s,",")); if ($where) { - if (!eregi("^[[:space:]]*WHERE",$where)) { + if (!preg_match("/^[[:space:]]*WHERE/i",$where)) { ## insert "WHERE" if not set $where="WHERE $where"; } $q.=" $where"; } } $this->_QDebug($q); return($q); } ## ## DELETE ## deletes the selected Table ## $check can be "soft" and "weak". Weak let's you delete the ## hole table, if you want ## function delete_Clause ($table,$where,$check="soft") { if (!$where && $check!="weak") { echo "ERROR: delete_Clause(): Parameter \$where is empty!
"; return(false); } $q=sprintf("DELETE FROM %s",$table); if ($where) { if (!eregi("^[[:space:]]*WHERE",$where)) { ## insert "WHERE" if not set $where="WHERE $where"; } $q.=" $where"; } $this->_QDebug($q); return($q); } ## ## This function checks wether in table $table a ## field $name is set with value $val ## ## it returns the number of found matches or zero ## function exists ($table,$name,$val) { $meta=$this->metadata_buffered($table); $j=$meta["meta"][$name]; list($k)=$this->convert($val,$meta[$j]); $q=sprintf("SELECT COUNT(%s) as c FROM %s WHERE %s=%s", $name,$table,$name,$k); $this->_QDebugs($q); $this->query($q); $this->next_record(); return($this->f("c")); } ## ## This function creates a query like exists, but returns ## an assoc array of the first found row, or false if nothing found ## field $name is set with value $val ## function getrow ($table,$name,$val) { $meta=$this->metadata_buffered($table); $j=$meta[meta][$name]; list($k)=$this->convert($val,$meta[$j]); $q=sprintf("SELECT * FROM %s WHERE %s=%s", $table,$name,$k); $this->_QDebug($q); $this->query($q); if ($this->next_record()) { return($this->Record); } else { echo "
WARNING: getrow(): KEY: $name VAL: $val not found
"; return(false); } } ## ## WHERE-PLAIN-CLAUSE ## Let you generate a WHERE-Clause with a Loop. ## ## Returns a where-clause beginning with " WHERE " ## ## This function generates a where-clause ## $mywhere An array of simple expressions, eg. "firstname='Alex'" ## $andor This string is printed bewtween the where-Array ## default is 'AND'. It will handle an existing ## $oldwhere correctly. You can set this to '', but then ## the correct operator must be set by you in the where ## $where an existing WHERE-clause. Default is empty. ## $check if 'strong', it will stop, if an empty where-clause ## will be returned, to avoid "full" selects. Default is soft ## function where_plain_Clause ($mywhere,$andor='AND',$where='',$check="soft") { $meta=$this->metadata_buffered($table); $q=''; for ($i=0; $i"; } - $q=ereg_Replace("^ $andor "," WHERE ",$q); + $q=preg_replace("/^ $andor /"," WHERE ",$q); $this->_QDebug("where_plain_Clause(): $q"); return($q); } ## ## ANOTHER-WHERE-CLAUSE ## ## This function generates a where-clause beginning with " WHERE " ## Different form where_plain_Clause() this function is fully automated ## It can handle NULL-Values (IS NULL) in a special manner: ## if a value of $fields is 'NULL', we are looking, if the ## operator is '='. In this case the operator is changed into "IS" ## in any other case it is changed into "IS NOT". ## ## $tables table ## $fields Assoc name=>value-fields ## $op Assoc name=>operator. If empty, '=' is taken. it is printed ## *between* the name/value pairs. ## if $op is 'func' the name is taken as function name, ## inside the brakets is the value. ## $special Affects the calculation of value. ## See INSERT_CLAUSE() for more about this. ## $andor This string is printed bewtween the name/value-pairs, ## default is 'AND'. If $where is set, it prints ## it directly at the end before concatenating ## $where an existing WHERE-clause. Default is empty. ## $check if 'strong', it will stop, if an empty where-clause ## will be returned, to avoid "full" selects. Default is soft ## ## Returns a where-clause beginning with " WHERE " ## function where_Clause ($table,$fields,$op='',$special='', $andor='AND',$where='',$check="soft") { $meta=$this->metadata_buffered($table); $q=''; if (!is_Array($op)) $op=ARRAY(); if (!is_Array($special)) $op=ARRAY(); if (!$andor) $andor='AND'; $i=0; for (reset($fields); list($key,$val)=each($fields); $i++) { list($k[val],$k[name])= $this->convert($fields[$key],$meta[$meta[meta][$key]],$special[$key]); if (!$op[$key]) $o='='; else $o=$op[$key]; if ('NULL'==strval($k[val])) { if ($o=='=' || strtoupper($o)=='IS') $o = 'IS'; else $o = 'IS NOT'; } $q.=" $andor $k[name] $o $k[val]"; } if ($where) { - $where=eregi_Replace("^[[:space:]]*WHERE","",$where); + $where=preg_replace("/^[[:space:]]*WHERE/i","",$where); $q.=" $andor $where"; } if (!$q && $ckeck=='full') { echo "WARNING: where_Clause(): WHERE-clause is empty!
"; } - $q=ereg_Replace("^ $andor "," WHERE ",$q); + $q=preg_replace("/^ $andor /"," WHERE ",$q); $this->_QDebug("where_Clause(): $q"); return($q); } ## ## capture-vars ## ## This function returns an assoc. Array consisting out of ## name=>value-pairs needed by all the other functions. It reads ## the name of the vars from the fields in $table and the values ## from the $GLOBALS-var-field. ## This has the sense, that you can name the variables in your ## Input-Form exactly like the names in your table. This again ## let make you less errors and less side effects. ## ## $table The name of the table ## function capture_vars ($table) { $meta=$this->metadata_buffered($table); $r=Array(); for ($i=0; $i < $meta["num_fields"]; $i++) { $j=$meta[$i]["name"]; if (isset($GLOBALS[$j])) { $r[$j] = $GLOBALS[$j]; $this->_QDebug("Found $j: $r[$j]"); } } return($r); } ## ## all_changed_vars ## ## This function returns an assoc. Array consisting out of ## name=>value-pairs which have a different value from the value ## currently existing in your table. This is needed by ## update_Clause(), cause with this, the update-query can be shortened ## to the maximum needed max. Can also be used for much other things, ## e.g. checking if something in your form has been changed (in this ## case it returns an empty array) ## ## $table The name of the table ## $fields Your assoc value field, which you want to check for ## $where The where-clause, which matches your row. ## This functions writes warnings, if your where-clause ## returns more than one row or nothing ## function all_changed_vars ($table,$fields,$where,$check='soft') { $meta=$this->metadata_buffered($table); $q1="SELECT * FROM $table $where"; $this->query($q1); $r=Array(); if ($this->next_record()) { for ($i=0; $i < $meta["num_fields"]; $i++) { $j=$meta[$i]["name"]; if ($this->Record[$j]!=$fields[$j]) { $r[$j]=$fields[$j]; $this->_QDebug("Changed $j: ".$fields[$j]." -> ".$this->Record[$j]); } } if ($this->next_record()) { echo "ERROR: all_changed_vars(): Found more than one row!
"; } } elseif ($check!='soft') { echo "
WARNING: all_changed_vars(): No row found!
"; } $this->_QDebug("WHERE: $where"); return($r); } ## ## metadata_buffered (internal) ## ## This function calls metadata() if it won't find the buffer, ## this speeds the Query-class strongly up, cause it is needed in nearly ## every function ## ## $table the name of the table ## ## Returns the metadata-field ## function metadata_buffered($table) { if ( !is_Array($this->meta_buf[$table]) || $this->meta_cache_off) { return ($this->meta_buf[$table]=$this->metadata($table,true)); } else { return ($this->meta_buf[$table]); } } } ?>