SoapFault exception: [Client] looks like we got no XML document in <document> has been already mentioned to occur when your server outputs something before <?xml ... > tag.
For all those having problems with that, and no access to the server code:
This is how to make a proxy that would clean responses for You
<?php
/**
* Simple class taken from a note by James Ellis [in __doRequest() page of manual]
*/
class Proxy_Client extends SoapClient {
protected $cacheDocument = "";
public function __construct($wsdl, $options) {
parent::__construct($wsdl, $options);
}
/**
* SetCacheDocument() sets the previously cached document contents
*/
public function SetCacheDocument($document) {
$this->cacheDocument = $document;
}
/**
* __doRequest() overrides the standard SoapClient to handle a local request
*/
public function __doRequest() {
return $this->cacheDocument;
}
}
//put this code in your function or wherever You have all required variables set
$client = new SoapClient($wsdl_url,$settings_array);
$void=$client->$method($params); //call this to get response from server
$response_string=$client->__getLastResponse();
//this part removes stuff
$start=strpos($response_string,'<?xml');
$end=strrpos($response_string,'>');
$response_string=substr($response_string,$start,$end-$start+1);
//get your proxy prepared
$proxy = new Proxy_Client($wsdl_url,$settings_array);
//and fill it with the server's response
$proxy->SetCacheDocument($response_string);
$and_finally_the_result_is=$proxy->$method($params);
print_r($and_finally_the_result_is); //this allows You to see what's there
?>
$method is the method's name eg. $method='getVersion';
$params - typical params for a soap method
SoapClient->__construct
(No version information available, might be only in CVS)
SoapClient->__construct — SoapClient コンストラクタ
説明
このコンストラクタは、WSDL モードもしくは 非 WSDL モードで SoapClient オブジェクトを生成します。
パラメータ
- wsdl
-
WSDL ファイルの URI もしくは 非 WSDL モードの場合 NULL
注意: 開発中は、WSDL のキャッシュを php.ini の soap.wsdl_cache_ttl で無効にしておくとよいでしょう。 そうしないと、WSDL を変更しても soap.wsdl_cache_ttl で設定した時間が経過するまで それが反映されなくなります。
- options
-
オプションの配列。もし WSDL モードで動作させる場合、 このパラメータはオプションです。非 WSDL モードで動作させる場合、 location と uri オプションを指定する必要があります。ここで、 location はリクエストを行う URL、 uri は SOAP サービスのターゲット名前空間です。
style および use オプション は非 WSDL モードでのみ動作します。 WSDL モードでは、これらは WSDL ファイルで指定されます。
soap_version オプションは、 SOAP 1.1 または SOAP 1.2 クライアントのどちらを使用するかを指定します。
HTTP 認証用として、login および password オプションが使用可能です。 プロキシサーバ経由で HTTP 接続を確立する場合は、 proxy_host, proxy_port, proxy_login および proxy_password の各オプションを使用してください。
compression オプションにより、 HTTP SOAP リクエストやレスポンスの圧縮を行うことができます。
encoding オプションは内部的な文字エンコーディングを定義します。 このオプションは SOAP リクエストのエンコーディング (常に utf-8) を変更しませんが、その中の文字列を変換します。
classmap オプションは WSDL 型を PHP クラスにマッピングするために使用可能です。 このオプションには、キーとしてWSDL 型、値として PHP クラスの名前を持つ配列を指定する必要があります。
boolean のオプション trace を設定すると、 SoapClient->__getLastRequest、 SoapClient->__getLastRequestHeaders、 SoapClient->__getLastResponse および SoapClient->__getLastResponseHeaders といったメソッドが使用できるようになります。
exceptions オプションは boolean 値で、soap のエラー時に SoapFault 型の例外をスローさせるかどうかを設定します。
connection_timeout オプションは、SOAP サービスに接続する際のタイムアウト秒数を指定します。 これを使用しても、レスポンスが遅いサービスのタイムアウトを定義することはできません。 サービスのコールが完了するまでの待ち時間を制限するには、設定項目 default_socket_timeout を使用します。
typemap オプションは、型マッピングの配列です。 この配列のキーは type_name、 type_ns (名前空間 URI)、from_xml (引数として文字列をひとつ受け取るコールバック) そして to_xml (引数としてオブジェクトをひとつ受け取るコールバック) です。
その他には stream_context や features、 cache_wsdl そして user_agent といったオプションがあります。
例
例1 SoapClient の例
<?php
$client = new SoapClient("some.wsdl");
$client = new SoapClient("some.wsdl", array('soap_version' => SOAP_1_2));
$client = new SoapClient("some.wsdl", array('login' => "some_name",
'password' => "some_password"));
$client = new SoapClient("some.wsdl", array('proxy_host' => "localhost",
'proxy_port' => 8080));
$client = new SoapClient("some.wsdl", array('proxy_host' => "localhost",
'proxy_port' => 8080,
'proxy_login' => "some_name",
'proxy_password' => "some_password"));
$client = new SoapClient("some.wsdl", array('local_cert' => "cert_key.pem"));
$client = new SoapClient(null, array('location' => "http://localhost/soap.php",
'uri' => "http://test-uri/"));
$client = new SoapClient(null, array('location' => "http://localhost/soap.php",
'uri' => "http://test-uri/",
'style' => SOAP_DOCUMENT,
'use' => SOAP_LITERAL));
$client = new SoapClient("some.wsdl",
array('compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP));
$server = new SoapClient("some.wsdl", array('encoding'=>'ISO-8859-1'));
class MyBook {
public $title;
public $author;
}
$server = new SoapClient("books.wsdl", array('classmap' => array('book' => "MyBook")));
?>
SoapClient->__construct
19-Jun-2008 10:48
09-Jun-2008 04:44
When using classmap option to map the SOAP results to a class, the constructor
of the object you've mapped to is not called.
$client = new SoapClient("url_to_wsdl",
array('classmap' => array('contact' => "Contact"));
$params = array("1");
$contact = $client->__soapCall("get_contact", $params);
Expected result:
A contact object that has properties initialized (i.e. db connections,
....).
Actual result:
A contact object without the properties.
Thanks for your help.
David Georges.
28-May-2008 02:23
> When using HTTP basic authentication, PHP will only send
> the credentials when invoking the service, not when
> fetching the WSDL.
The same goes for using an SSL client certficate, the SoapClient will only present the certificate on the actual remote call, not when getting the WSDL. The workaround is the same as above. HttpRequest works as expected.
30-Apr-2008 05:24
The "cache_wsdl" option takes constants like WSDL_CACHE_NONE or WSDL_CACHE_DISK that are listed on the "SOAP constants" page -> /manual/en/soap.constants.php
30-Jan-2008 10:10
A note regarding boolean values that may seem obvious on reflection but could be a gotcha for some:
Seeing a SOAP request example with <SomeBooleanParam>true</SomeBooleanParam> may lead you to pass in string "true" or "false" as the parameter, which is incorrect - the correct method is to use boolean data types.
<?php
$client = new SoapClient($wsdl,$options);
$method = "DoSomething";
$params = new stdClass;
$params->SomeBooleanParam = TRUE;
$client->$method($params);
/**
simplified request snippet would be
<SomeBooleanParam>true</SomeBooleanParam>
**/
//this will also be correct, but not for the right reasons:
$params->SomeBooleanParam = "true";
$client->$method($params);
/**
simplified request snippet would be
<SomeBooleanParam>true</SomeBooleanParam>
**/
//this is where you may be wondering what is going on
$params->SomeBooleanParam = "false";
$client->$method($params);
/**
simplified request snippet would be
<SomeBooleanParam>true</SomeBooleanParam>
**/
//you need to do this instead
$params->SomeBooleanParam = FALSE;
$client->$method($params);
/**
simplified request snippet would be
<SomeBooleanParam>false</SomeBooleanParam>
**/
?>
Hope that helps!
20-Dec-2007 07:22
If you want to use compression there is a missing parameter in the example above:
<?php
$client = new SoapClient("some.wsdl",
array('compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5));
?>
If you use SOAP_COMPRESSION_GZIP you have to add the compression level. Otherwise it will not compress the request. Took me some hours ;)
Stephan
09-Jul-2007 03:46
Oops!
I have written the wrong exception message in my last post.
The correct exception is:
SoapFault exception: [Client] looks like we got no XML document in <document>
09-Jul-2007 03:28
If you get the following exception:
'Cannot open the XML file: the file is not well-formatted!'
PHP files of your SOAP-server contains syntax-error (probably).
If everything looks ok, try to remove EVERY blank-space and new-line outside the <?php ... ?> tag... this should save you an headache!
09-Feb-2007 12:20
As noted in the bug report http://bugs.php.net/bug.php?id=36226, it is considered a feature that sequences with a single element do not come out as arrays. To override this "feature" you can do the following:
$x = new SoapClient($wsdl, array('features' =>
SOAP_SINGLE_ELEMENT_ARRAYS));
01-Dec-2006 05:26
When using classmap to map the SOAP results to a class, the constructor of the object you've mapped to is _not_ called. This applies to the PHP5 __construct() and the PHP4 ClassName() constructors.
01-Mar-2006 09:57
I kept having a problem using an HTTP proxy with SOAP. The proxy_port parameter has to be an integer, ie. "proxy_port"=>"80" won't work, you'll have to use "proxy_port"=>80.
HTH,
Marius
22-Dec-2005 11:40
If you're using CLI and there are multiple IP addresses available for outgoing SOAP-requests, try this "secret" to set outgoing IP:
e.g. for local IP 10.1.4.71:
$opts = array('socket' => array('bindto' => '10.1.4.71:0'));
$context = stream_context_create($opts);
$client = new SoapClient(null, array('location'=>'http://...','uri' => '...','stream_context' => $context));
You can also set other options for the stream context, please refer to this page:
Appendix M: http://www.php.net/manual/en/wrappers.php
Bye,
Nils Sowen
29-Aug-2005 06:31
If you connect to a SoapServer, that has been created with session persistence, you can access the server's session id via SoapClient->_cookies[<session_id_name>][0]. This property becomes available after your first client method call.
I have only tested this with session.use_cookies=1.
12-Aug-2005 03:31
When using HTTP basic authentication, PHP will only send the credentials when invoking the service, not when fetching the WSDL.
To solve this, one needs to fetch the WSDL manually (or using PHP, of course!) and store it on the file system. Obviously, the first parameter for SoapClient(..) then needs to refer to that local copy.
Alternatively, the user annotations at http://nl3.php.net/manual/en/ref.soap.php state that one could encode the login and password into the URL as well.
See http://bugs.php.net/bug.php?id=27777
12-Jul-2005 04:56
As of version 5.0.4 you can now dynamically change your location even if you're using a wsdl
<?php
$client = new SoapClient("http://some.host.net/wsdl/somefile.wsdl",array(
"location" => 'myurl.com'));
?>
notice you can now use the "location" key.
This means you can have the same wsdl file not define a location until runtime which is great if you have a development test site or if you distribute your files to other companies.
Prior to this change you would have to ship a custom wsdl file to every client you had with their location hardcoded.
05-Mar-2005 07:30
Using a WSDL file is the way to go, however, for my particular application, the LOCATION:PORT needed to be dynamic so that my SOAP clients would be able to call a different service based on the client domain.
If you are using a WSDL, SoapClient() requires a URL direct to an actual URL and does not let you use a PHP file that outputs the dynamic WSDL XML in its stead. So, I ended up making a separate WSDL for each possible service needed and had to maintain them all if the service description changed.
Finally, after some fiddling, I was able to create a PHP page with the proper Mime type headers so that I could then trick SoapClient() to think that it was being passed a file with a ".wsdl" extension.
Example:
<?php
// filename: wsdl.php
header('Content-Type: application/xml; charset=UTF-8');
header('Content-Disposition: attachment; filename="filename.wsdl"');
$wsdl = 'path/wsdl_name.wsdl';
// read in file
$handle = fopen($wsdl, "r");
$wsdl_xml = fread($handle, filesize($wsdl));
fclose($handle);
// put code here to replace url and port in xml
echo $wsdl_xml;
?>
Now, in order to make this work, you can't just call a relative path to the file. I believe it has to go through Apache to properly set the mime type headers, etc... So you would use a full "http://....." address as the path to the wsdl.php file.
<?php
//... somewhere in your soap client code
$wsdl_loc = "http://yourdomain.com/wsdl.php";
$soap_client = new SoapClient($wsdl_loc, $client_param_array);
?>
Another, perhaps not so clean, way of achieving this would be to modify your .htaccess file in the directory where your WSDL file exists to force ".wsdl" files to run through the PHP engine. Add the following to your .htaccess:
AddType application/x-httpd-php .php .wsdl
You can then put dynamic PHP code snippets in your *.wsdl files to change whatever values you need to.
There are pros and cons to each solutions. The Mime solution probably taxes the system more as it has to read the file in every time a soap request is made. The htaccess solution makes it so you have to depend on either a modified .htaccess or Apache conf file.
Perhaps if you set the "soap.wsdl_cache_enabled", using ini_set(), to 1 (default), the caching will make it so it doesn't read the file every time for the Mime solution.
