Re: [SOAP] how to send the request through XML using command prompt.

[email protected] (Richard Quadling) Thu, 17 Feb 2011 10:47:38 +0000
Newsgroups php.soap
Message-ID <[email protected]>
On 17 February 2011 06:33, valluriravi <[email protected]> wrote:
>
> Hi All,
>
>       We are new to soap with PHP. As per our reqirment we need how to send
> the request through XML
> with soap and how to get the response as XML through soap using command
> prompt.
>
> We are having WSDL link with us.
>
> http://www.timevalue.com/webservices/tve.asmx?WSDL
>
> Please send any reference Links for this requirment.
>
> Regards
> Ravi.

First of all, make sure you can run PHP from the command line. I use
Windows and the documentation on pages [1] and [2] cover the necessary
elements/setup to make effective use of PHP at the command line. If
you are not on Windows, then you need to read [3].

In terms of creating something that can use the wsdl file you
provided, I'd recommend using the wsdl2php from sourceforge ([4] and
[5]). I've attached my copy of wsdl2php to this email (if it doesn't
get through to you directly let me know).

Having got wsdl2php installed and have PHP configured along the lines
I've suggested in the documentation, then you need to build your local
classes for the service.

You do this by ...

MD C:\wsdl\timevalue
CD C:\wsdl\timevalue
C:\PHP5\php.exe -f C:\PHP5\PEAR\wsdl2php --
http://www.timevalue.com/webservices/tve.asmx?WSDL

You'll now have a file called C:\wsdl\timevalue\tve.php [6]

This contains all the classes you need. Take a look through it. You
can see how it has created a SoapClient subclass and a class for each
complex type used by the service as well as the response types for
each service method.

The service class is called tve and it has 4 methods.

I use a simple script [7] to prove the service by calling the Hello
World method.

I call the script C:\wsdl\timevalue\consume.php

Running consume (or C:\PHP5\php.exe -f C:\wsdl\timevalue\consume.php
if you haven't done all the things needed for full CLI integration),
will produce an output of [8].

From here on, it is just a case of working out which methods you want
to use, creating the appropriate parameter objects and populating them
just like you would for any other PHP class.


As the service returns a cookie, you may need to extract this and
supply it with your subsequent calls.

	// A session cookie is supplied in the response header, extract this
and add it to the service via __setCookie() on subsequent requests.
	preg_match('`Set-Cookie:
(?P<Cookie>(?P<CookieName>[^=]*)=(?P<CookieValue>[^;]*);)`',
$o_Service->__getLastResponseHeaders(), $a_CookieMatch);
	$o_Service->__setCookie($a_CookieMatch['CookieName'],
$a_CookieMatch['CookieValue']);

You may have to extend this if more than 1 cookie is supplied.


I hope this helps you somewhat. If you get stuck, please come back.

Try the code I've supplied before attempting to integrate it into any
of your code. If you can't get this working, then integrating non
working code will only confuse things more.

Regards,

Richard.


[1] http://docs.php.net/manual/en/install.windows.manual.php
[2] http://docs.php.net/manual/en/install.windows.commandline.php
[3] http://docs.php.net/manual/en/install.unix.commandline.php
[4] http://www.urdalen.no/wsdl2php/
[5] http://sourceforge.net/projects/wsdl2php/
[6] http://pastebin.com/WiYgK34g
[7] http://pastebin.com/Ane2JUeR
[8] http://pastebin.com/MfwrJimM
-- 
Richard Quadling
Twitter : EE : Zend
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY
wsdl2php.php (application/x-httpd-php, 16.7 KB)
<?php
// +------------------------------------------------------------------------+
// | wsdl2php                                                               |
// +------------------------------------------------------------------------+
// | Copyright (C) 2005 Knut Urdalen <[email protected]>               |
// +------------------------------------------------------------------------+
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT       | 
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT    |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   |
// +------------------------------------------------------------------------+
// | This software is licensed under the LGPL license. For more information |
// | see http://wsdl2php.sf.net                                             |
// +------------------------------------------------------------------------+

ini_set('soap.wsdl_cache_enabled', 0); // disable WSDL cache

if( $_SERVER['argc'] != 2 )
	{
	die("usage: wsdl2php <wsdl-file>\n");
	}

$wsdl = $_SERVER['argv'][1];

print "Analyzing WSDL";

// As we are going to need to display XML, use Tidy to make it pretty.
$a_TidyConfig = array
	(
	'indent'            => 4,
	'indent-attributes' => true,
	'input-xml'         => true,
	'output-xml'        => true,
	'wrap'              => 0,
	);
$o_Tidy = new Tidy;

try
	{
	$client = new SoapClient
		(
		$wsdl,
		array
			(
			'encoding'  => 'ISO-8859-1',
			'exception' => True,
			'trace'     => True,
			)
		);
}
catch(SoapFault $e)
	{
	echo
		// Show exception.
		'Exception', PHP_EOL,
		'---------', PHP_EOL,
		$e->getMessage(), PHP_EOL;
	}
catch(Exception $e)
	{
	echo
		// Show trace.
		PHP_EOL,
		'Request Headers', PHP_EOL,
		'---------------', PHP_EOL,
		$client->__getLastRequestHeaders(), PHP_EOL,
		'Request', PHP_EOL,
		'-------', PHP_EOL,
		$o_Tidy->repairString($client->__getLastRequest(), $a_TidyConfig), PHP_EOL, PHP_EOL,
		'Response Headers', PHP_EOL,
		'----------------', PHP_EOL,
		$client->__getLastResponseHeaders(), PHP_EOL,
		'Response', PHP_EOL,
		'--------', PHP_EOL,
		$o_Tidy->repairString($client->__getLastResponse(), $a_TidyConfig), PHP_EOL, PHP_EOL, PHP_EOL,

		// Show exception.
		'Exception', PHP_EOL,
		'---------', PHP_EOL,
		$e->getMessage(), PHP_EOL;
	}

print ".";
$dom = new DOMDocument();
$dom->load($wsdl);
print ".";

// get documentation
$nodes = $dom->getElementsByTagName('documentation');
$doc = array('service' => '',
	     'operations' => array());
foreach($nodes as $node) {
  if( $node->parentNode->localName == 'service' ) {
    $doc['service'] = trim($node->parentNode->nodeValue);
  } else if( $node->parentNode->localName == 'operation' ) {
    $operation = $node->parentNode->getAttribute('name');
    //$parameterOrder = $node->parentNode->getAttribute('parameterOrder');
    $doc['operations'][$operation] = trim($node->nodeValue);
  }
}
print ".";

// get targetNamespace
$targetNamespace = '';
$nodes = $dom->getElementsByTagName('definitions');
foreach($nodes as $node) {
  $targetNamespace = $node->getAttribute('targetNamespace');
}
print ".";

// declare service
$service = array('class' => $dom->getElementsByTagNameNS('*', 'service')->item(0)->getAttribute('name'),
		 'wsdl' => $wsdl,
		 'doc' => $doc['service'],
		 'functions' => array());
print ".";

// PHP keywords - can not be used as constants, class names or function names!
$reserved_keywords = array('and', 'or', 'xor', 'as', 'break', 'case', 'cfunction', 'class', 'continue', 'declare', 'const', 'default', 'do', 'else', 'elseif', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'extends', 'for', 'foreach', 'function', 'global', 'if', 'new', 'old_function', 'static', 'switch', 'use', 'var', 'while', 'array', 'die', 'echo', 'empty', 'exit', 'include', 'include_once', 'isset', 'list', 'print', 'require', 'require_once', 'return', 'unset', '__file__', '__line__', '__function__', '__class__', 'abstract', 'private', 'public', 'protected', 'throw', 'try');

// ensure legal class name (I don't think using . and whitespaces is allowed in terms of the SOAP standard, should check this out and may throw and exception instead...)
$service['class'] = str_replace(' ', '_', $service['class']);
$service['class'] = str_replace('.', '_', $service['class']);
$service['class'] = str_replace('-', '_', $service['class']);

if(in_array(strtolower($service['class']), $reserved_keywords)) {
  $service['class'] .= 'Service';
}

// verify that the name of the service is named as a defined class
if(class_exists($service['class'], false)) {
  throw new Exception("Class '".$service['class']."' already exists");
}

/*if(function_exists($service['class'])) {
  throw new Exception("Class '".$service['class']."' can't be used, a function with that name already exists");
}*/

// get operations
$operations = $client->__getFunctions();
foreach($operations as $operation) {

  /*
   This is broken, need to handle
   GetAllByBGName_Response_t GetAllByBGName(string $Name)
   list(int $pcode, string $city, string $area, string $adm_center) GetByBGName(string $Name)

   finding the last '(' should be ok
   */
  //list($call, $params) = explode('(', $operation); // broken
  
  //if($call == 'list') { // a list is returned
  //}
  
  /*$call = array();
  preg_match('/^(list\(.*\)) (.*)\((.*)\)$/', $operation, $call);
  if(sizeof($call) == 3) { // found list()
    
  } else {
    preg_match('/^(.*) (.*)\((.*)\)$/', $operation, $call);
    if(sizeof($call) == 3) {
      
    }
  }*/

  $matches = array();
  if(preg_match('/^(\w[\w\d_]*) (\w[\w\d_]*)\(([\w\$\d,_ ]*)\)$/', $operation, $matches)) {
    $returns = $matches[1];
    $call = $matches[2];
    $params = $matches[3];
  } else if(preg_match('/^(list\([\w\$\d,_ ]*\)) (\w[\w\d_]*)\(([\w\$\d,_ ]*)\)$/', $operation, $matches)) {
    $returns = $matches[1];
    $call = $matches[2];
    $params = $matches[3];
  } else { // invalid function call
    throw new Exception('Invalid function call: '.$function);
  }

  $params = $params ? explode(', ', $params) : array();

  $paramsArr = array();
  foreach($params as $param) {
    $paramsArr[] = explode(' ', $param);
  }
  //  $call = explode(' ', $call);
  $function = array('name' => $call,
		    'method' => $call,
		    'return' => $returns,
		    'doc' => isset($doc['operations'][$call])?$doc['operations'][$call]:'',
		    'params' => $paramsArr);

  // ensure legal function name
  if(in_array(strtolower($function['method']), $reserved_keywords)) {
    $function['name'] = '_'.$function['method'];
  }

  // ensure that the method we are adding has not the same name as the constructor
  if(strtolower($service['class']) == strtolower($function['method'])) {
    $function['name'] = '_'.$function['method'];
  }

  // ensure that there's no method that already exists with this name
  // this is most likely a Soap vs HttpGet vs HttpPost problem in WSDL
  // I assume for now that Soap is the one listed first and just skip the rest
  // this should be improved by actually verifying that it's a Soap operation that's in the WSDL file
  // QUICK FIX: just skip function if it already exists
  $add = true;
  foreach($service['functions'] as $func) {
    if($func['name'] == $function['name']) {
      $add = false;
    }
  }
  if($add) {
    $service['functions'][] = $function;
  }
  print ".";
}

$types = $client->__getTypes();

$primitive_types = array('string', 'int', 'long', 'float', 'boolean', 'dateTime', 'double', 'short', 'UNKNOWN', 'base64Binary', 'decimal', 'ArrayOfInt', 'ArrayOfFloat', 'ArrayOfString', 'decimal', 'hexBinary'); // TODO: dateTime is special, maybe use PEAR::Date or similar
$service['types'] = array();
foreach($types as $type) {
  $parts = explode("\n", $type);
  $class = explode(" ", $parts[0]);
  $class = $class[1];
  
  if( substr($class, -2, 2) == '[]' ) { // array skipping
    continue;
  }

  if( substr($class, 0, 7) == 'ArrayOf' ) { // skip 'ArrayOf*' types (from MS.NET, Axis etc.)
    continue;
  }

  if(classExists($class, $service['types'])) { // can't redeclare classes
    continue;
  }

  $members = array();
  for($i=1; $i<count($parts)-1; $i++) {
    $parts[$i] = trim($parts[$i]);
    list($type, $member) = explode(" ", substr($parts[$i], 0, strlen($parts[$i])-1) );

    // check syntax
    if(preg_match('/^$\w[\w\d_]*$/', $member)) {
      throw new Exception('illegal syntax for member variable: '.$member);
      continue;
    }

    // IMPORTANT: Need to filter out namespace on member if presented
    if(strpos($member, ':')) { // keep the last part
      list($tmp, $member) = explode(':', $member);
    }

    // OBS: Skip member if already presented (this shouldn't happen, but I've actually seen it in a WSDL-file)
    // "It's better to be safe than sorry" (ref Morten Harket) 
    $add = true;
    foreach($members as $mem) {
      if($mem['member'] == $member) {
	$add = false;
      }
    }
    if($add) {
      $members[] = array('member' => $member, 'type' => $type);
    }
  }

  // gather enumeration values
  $values = array();
  if(count($members) == 0) {
    $values = checkForEnum($dom, $class);
  }

  $service['types'][] = array('class' => $class, 'members' => $members, 'values' => $values);
  print ".";
}
print "done\n";

print "Generating code...";
$code = "";

// add types
foreach($service['types'] as $type) {
  //  $code .= "/**\n";
  //  $code .= " * ".(isset($type['doc'])?$type['doc']:'')."\n";
  //  $code .= " * \n";
  //  $code .= " * @package\n";
  //  $code .= " * @copyright\n";
  //  $code .= " */\n";

  // add enumeration values
  $code .= "class ".$type['class']." {\n";
  foreach($type['values'] as $value) {
    $code .= "  const ".generatePHPSymbol($value)." = '$value';\n";
  }
  
  // add member variables
  foreach($type['members'] as $member) {
    //$code .= "  /* ".$member['type']." */\n";
    $code .= "  public \$".$member['member']."; // ".$member['type']."\n";
  }
  $code .= "}\n\n";

  /*  print "Writing ".$type['class'].".php...";
  $filename = $type['class'].".php";
  $fp = fopen($filename, 'w');
  fwrite($fp, "<?php\n".$code."?>\n");
  fclose($fp);
  print "ok\n";*/
}

// add service

// page level docblock
//$code .= "/**\n";
//$code .= " * ".$service['class']." class file\n";
//$code .= " * \n";
//$code .= " * @author    {author}\n";
//$code .= " * @copyright {copyright}\n";
//$code .= " * @package   {package}\n";
//$code .= " */\n\n";


// require types
//foreach($service['types'] as $type) {
//  $code .= "/**\n";
//  $code .= " * ".$type['class']." class\n";
//  $code .= " */\n";
//  $code .= "require_once '".$type['class'].".php';\n";
//}

$code .= "\n";

// class level docblock
$code .= "/**\n";
$code .= " * ".$service['class']." class\n";
$code .= " * \n";
$code .= parse_doc(" * ", $service['doc']);
$code .= " * \n";
$code .= " * @author    {author}\n";
$code .= " * @copyright {copyright}\n";
$code .= " * @package   {package}\n";
$code .= " */\n";
$code .= "class ".$service['class']." extends SoapClient {\n\n";

// add classmap
$code .= "  private static \$classmap = array(\n";
foreach($service['types'] as $type) {
  $code .= "                                    '".$type['class']."' => '".$type['class']."',\n";
}
$code .= "                                   );\n\n";
$code .= "  public function ".$service['class']."(\$wsdl = \"".$service['wsdl']."\", \$options = array()) {\n";

// initialize classmap (merge)
$code .= "    foreach(self::\$classmap as \$key => \$value) {\n";
$code .= "      if(!isset(\$options['classmap'][\$key])) {\n";
$code .= "        \$options['classmap'][\$key] = \$value;\n";
$code .= "      }\n";
$code .= "    }\n";
$code .= "    parent::__construct(\$wsdl, \$options);\n";
$code .= "  }\n\n";

foreach($service['functions'] as $function) {
  $code .= "  /**\n";
  $code .= parse_doc("   * ", $function['doc']);
  $code .= "   *\n";

  $signature = array(); // used for function signature
  $para = array(); // just variable names
  if(count($function['params']) > 0) {
    foreach($function['params'] as $param) {
      $code .= "   * @param ".(isset($param[0])?$param[0]:'')." ".(isset($param[1])?$param[1]:'')."\n";
      /*$typehint = false;
      foreach($service['types'] as $type) {
	if($type['class'] == $param[0]) {
	  $typehint = true;
	}
      }
      $signature[] = ($typehint) ? implode(' ', $param) : $param[1];*/
      $signature[] = (in_array($param[0], $primitive_types) or substr($param[0], 0, 7) == 'ArrayOf') ? $param[1] : implode(' ', $param);
      $para[] = $param[1];
    }
  }
  $code .= "   * @return ".$function['return']."\n";
  $code .= "   */\n";
  $code .= "  public function ".$function['name']."(".implode(', ', $signature).") {\n";
  //  $code .= "    return \$this->client->".$function['name']."(".implode(', ', $para).");\n";
  $code .= "    return \$this->__soapCall('".$function['method']."', array(";
  $params = array();
  if(count($signature) > 0) { // add arguments
    foreach($signature as $param) {
      if(strpos($param, ' ')) { // slice 
        $tmp_param = explode(' ', $param);
	$param = array_pop($tmp_param);
      }
      $params[] = $param;
    }
    //$code .= "\n      ";
    $code .= implode(", ", $params);
    //$code .= "\n      ),\n";
  }
  $code .= "), ";
  //$code .= implode(', ', $signature)."),\n";
  $code .= "      array(\n";
  $code .= "            'uri' => '".$targetNamespace."',\n";
  $code .= "            'soapaction' => ''\n";
  $code .= "           )\n";
  $code .= "      );\n";
  $code .= "  }\n\n";
}
$code .= "}\n\n";
print "done\n";

print "Writing ".$service['class'].".php...";
$fp = fopen($service['class'].".php", 'w');
fwrite($fp, "<?php\n".$code."?>\n");
fclose($fp);
print "done\n";

function parse_doc($prefix, $doc) {
  $width = 100;

  // Tag OL.
  $doc = preg_replace('`^(\d++\.)`sim', '<OL>\1', $doc);

  // Tag UL.
  $doc = preg_replace('`^- `sim', '<UL>', $doc);

  // Join broken lines (but not OL or UL tags).
  $doc = preg_replace('`([^\n])\n(?!\n|<OL>|<UL>)`sim', '\1 \2', $doc);

  // Break string into lines.
  $lines = preg_split('`\r\n|\r|\n`sim', $doc);

  // Word wrap each line to 96 characters, 92 for <OL>
  $lines = array_map(
    function($line)
    use($width, $prefix) {
      if (in_array(substr($line, 0, 4), array('<OL>', '<UL>'))) {
        $line = substr_replace($line, '    ', 0, 4);
        $width -= strlen($prefix) + 4;
        $prefix = '       ';
      } else {
        $width -= strlen($prefix);
        $prefix = '';
      }
      return wordwrap($line, $width, "\n$prefix", False);
    },
    $lines
  );

  // Join and break again to account for <OL>
  $lines = preg_split('`\r\n|\r|\n`sim', implode("\n", $lines));

  // Add prefix
  $lines = array_map(
    function($line)
    use($prefix) {
      return $prefix . $line;
    },
    $lines
  );

  // Join
  $doc = implode("\n", $lines);

  return $doc . "\n";
}
/**
 * Look for enumeration
 * 
 * @param DOM $dom
 * @param string $class
 * @return array
 */
function checkForEnum(&$dom, $class) {
  $values = array();
  
  $node = findType($dom, $class);
  if(!$node) {
    return $values;
  }
  
  $value_list = $node->getElementsByTagName('enumeration');
  if($value_list->length == 0) {
    return $values;
  }

  for($i=0; $i<$value_list->length; $i++) {
    $values[] = $value_list->item($i)->attributes->getNamedItem('value')->nodeValue;
  }
  return $values;
}

/**
 * Look for a type
 * 
 * @param DOM $dom
 * @param string $class
 * @return DOMNode
 */
function findType(&$dom, $class) {
  $types_node  = $dom->getElementsByTagName('types')->item(0);
  $schema_list = $types_node->getElementsByTagName('schema');
  
  for ($i=0; $i<$schema_list->length; $i++) {
    $children = $schema_list->item($i)->childNodes;
    for ($j=0; $j<$children->length; $j++) {
      $node = $children->item($j);
      if ($node instanceof DOMElement &&
	  $node->hasAttributes() &&
	  $node->attributes->getNamedItem('name')->nodeValue == $class) {
	return $node;
      }
    }
  }
  return null;
}

function generatePHPSymbol($s) {
  global $reserved_keywords;
  
  if(!preg_match('/^[A-Za-z_]/', $s)) {
    $s = 'value_'.$s;
  }
  if(in_array(strtolower($s), $reserved_keywords)) {
    $s = '_'.$s;
  }
  return preg_replace('/[-.\s]/', '_', $s);
}

function classExists($classname, $service_types) {
  foreach($service_types as $service_type) {
    if($service_type['class'] == $classname) {
      return true;
    }
  }
  return false;
}

?>