I need to test my php API from cli.
This is my php script test.php:
<?php
$request = new Request();
if (isset($_SERVER['PATH_INFO'])) {
$request->url_elements = explode('/', trim($_SERVER['PATH_INFO'], '/'));
}
$request->method = strtoupper($_SERVER['REQUEST_METHOD']);
switch ($request->method) {
case 'GET':
$request->parameters = $_GET;
break;
case 'POST':
$request->parameters = $_POST;
break;
case 'PUT':
parse_str(file_get_contents('php://input'), $request->parameters);
break;
}
print $request->method . ": "; print_r($request->parameters); # DEBUG
?>
This is my attempt, using curl (as extensively documented on the web...):
$ curl -X POST -H "Content-type: application/json" -d '{"key":"value"}' http://localhost/test.php
And this is the result:
_GET: Array
(
)
_POST: Array
(
)
I would expect, instead, "key: value" in _POST...
What do I miss?
P.S.: sorry, I know I'm doing some very silly mistake, I feel very dumb... :-(
You're POSTing JSON but trying to interpret urlform-encoded data. You should use $postdata = file_get_contents("php://input");
You shouldn't test REST APIs this way. The testing code must not contain any URI structure. By REST clients you always have to follow the links given by the API, and find the proper link based on the metadata (e.g. link relations, RDF, etc...) attached to it. If you cannot follow basic REST constraints (uniform interface constraint in this case), why do you call your API as REST?
In your case the GET http://example.com/api/v1/ should return a link something like this:
{
relation: "http://example.com/api/v1/docs/createItem"
uri: "http://example.com/api/v1/",
method: "POST",
headers: {
contentType: "application/json"
},
data: {
key: "value"
}
}
Your testing code should be similar to this:
$apiRoot = 'http://example.com/api/v1/'
$response1 = getHttp($apiRoot);
expect($response1->headers->statusCode)->toBe(200);
$data1= parseJson($response1);
$link2 = findLinkByRelation($data1, $apiRoot.'docs/myCollection/createItem');
$response2 = followLink($link2);
expect($response2->headers->statusCode)->toBe(201);
$data2 = parseJson($response2);
$link3 = findLinkByRelation($data2, $apiRoot.'docs/myCollection/getItem');
$response3 = followLink($link3);
expect($response3->headers->statusCode)->toBe(200);
$data3 = parseJson($response3);
expect($data3)->containProperty(array("key" => "value"));
This way the testing code will be loosely coupled to the service implementation just like real clients, so it can be used as a template of real clients.
Btw. this is called end to end testing of your service. You can make it faster if you mock out the HTTP part by overriding the superglobals, like $_SERVER, $_POST, etc... in your tests.
Ohh, I read your question. The $_POST parses only application/x-www-form-urlencoded and multipart/form-data. So you have to get the raw post data with the input stream and parse it manually, but what you really need is a HTTP framework, e.g. http://www.slimframework.com/ , http://symfony.com/ , etc... which does this automatically. But that's not about how you should test your API. :-)
Related
In the Zend Framework 2, using REST Client using the POST method, the post URL has the query string,
As normal in postman when the request is post, the post content i have set
{"authToken":"11111111111111"} and post, status success.
when i get this value using GET method i have get the response, {"authToken":"11111111111111"}
which is correct.
when i run this through, php zend 2 using below method,
protected function genericSendRequest($url, $rawJsonData = NULL, $param = array()){
$response = '';
if(!empty($param)) {
$url .= '?' . http_build_query($param);
}
$client = $this->getHttpClient();
$client->setUri($url);
$client->setMethod('POST');
if($rawJsonData != null) {
$client->setRawBody(json_encode($rawJsonData));
}
$response = $client->send();
if ($response->isSuccess()) {
return $response->getContent();
}
return NULL;
}
Request Input,
$rawJsonData = array("authToken"=>"11111111111111");
$param = array("id"=>"9e770c9f71b4ef1b4ae85c58b0be4280253f9a2e");
Response i got,
id=9e770c9f71b4ef1b4ae85c58b0be4280253f9a2e&%7B%22authToken%22%3A%2211111111111111%22%7D=
This look like a URL query string,
i need the the response {"authToken":"11111111111111"}
ie. I need to get what i have post but i am getting post + query string,
query string is additionally posting to the request, that so its getting in the response,
how should avoid posting this querying string.
what the change needed in my code.?
Any help much appreciated.
Thanks in advance.
You question isn't very clear, but assuming you want to POST JSON data rather than form encoded data, you want:
if ($rawJsonData != null) {
$client->setRawBody(json_encode($rawJsonData));
}
Also, you could rewrite the query string part of your function to just:
if (!empty($param)) {
$uri .= '?' . http_build_query($param);
}
If I was to go about displaying just MY github repositories and their contents on an external website how would i go about doing this? Are there any source code's you can provide me with, if not point me in the right direction? I'm quite a beginner to programming so any help is appreciated. Thank you everyone.
Taking a glance at their website
I glanced over relevant links- but still have no clue how I would accomplish this.
-Github List all Repo's
-Github List all Repo content
all of the previous answers are great. however if you are looking for a quick and dirty example of how to get a list of publicly available repos then check out my jsfiddle.
which uses this ajax call to list all of a users public repos:
$("#btn_get_repos").click(function() {
$.ajax({
type: "GET",
url: "https://api.github.com/users/google/repos",
dataType: "json",
success: function(result) {
for(var i in result ) {
$("#repo_list").append(
"<li><a href='" + result[i].html_url + "' target='_blank'>" +
result[i].name + "</a></li>"
);
console.log("i: " + i);
}
console.log(result);
$("#repo_count").append("Total Repos: " + result.length);
}
});
});
to see what kind of data is returned just check the console after clicking the button or you can install Google Chromes JSONView extension and then just visit the url that the ajax request is making i.e. https://api.github.com/users/google/repos
Here is a nice way just with the curl. You should change the $user and the $token variableso to make this script work for your case. The code is tested with a valid token so I hope it will work for you. As you could see in the comments of the code the token could be generated from your github account from here https://github.com/settings/applications
<?php
// for example your user
$user = 'flesheater';
// A token that you could generate from your own github
// go here https://github.com/settings/applications and create a token
// then replace the next string
$token = 'ced38b0e522a5c5e8ab10';
// We generate the url for curl
$curl_url = 'https://api.github.com/users/' . $user . '/repos';
// We generate the header part for the token
$curl_token_auth = 'Authorization: token ' . $token;
// We make the actuall curl initialization
$ch = curl_init($curl_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// We set the right headers: any user agent type, and then the custom token header part that we generated
curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent: Awesome-Octocat-App', $curl_token_auth));
// We execute the curl
$output = curl_exec($ch);
// And we make sure we close the curl
curl_close($ch);
// Then we decode the output and we could do whatever we want with it
$output = json_decode($output);
if (!empty($output)) {
// now you could just foreach the repos and show them
foreach ($output as $repo) {
print '' . $repo->name . '<br />';
}
}
?>
Also since we like github, we should cache the results in the end and fetch them once per day or so.
All these examples are just as pseudo without "authentication" and you can improve them yourself as you like;
<?php
// a simple way to get a user's repo
$res = file_get_contents("https://api.github.com/repos/qeremy/mii");
$res = json_decode($res);
print_r($res);
?>
stdClass Object
(
[language] => JavaScript
[merges_url] => https://api.github.com/repos/qeremy/mii/merges
[contributors_url] => https://api.github.com/repos/qeremy/mii/contributors
[assignees_url] => https://api.github.com/repos/qeremy/mii/assignees{/user}
[url] => https://api.github.com/repos/qeremy/mii
[description] => Multipurpose JavaScript Library
[ssh_url] => git#github.com:qeremy/mii.git
[comments_url] => https://api.github.com/repos/qeremy/mii/comments{/number}
[statuses_url] => https://api.github.com/repos/qeremy/mii/statuses/{sha}
[keys_url] => https://api.github.com/repos/qeremy/mii/keys{/key_id}
...
<?php
// getting a repo's README
$res = file_get_contents("https://api.github.com/repos/qeremy/mii/readme");
$res = json_decode($res);
print_r($res);
?>
stdClass Object
(
[_links] => stdClass Object
(
[self] => https://api.github.com/repos/qeremy/mii/contents/README.md
[git] => https://api.github.com/repos/qeremy/mii/git/blobs/49f0c4d5e25ac44921ba4372aebd76d2da5128e2
[html] => https://github.com/qeremy/mii/blob/master/README.md
)
[url] => https://api.github.com/repos/qeremy/mii/contents/README.md
[type] => file
[sha] => 49f0c4d5e25ac44921ba4372aebd76d2da5128e2
[path] => README.md
[size] => 8213
[encoding] => base64
[content] => QWN0dWFsbHksIEkga25vdyB0aGF0IHRoZXJlIGFyZSBidWNoIG9mIEphdmFT
Y3JpcHQgbGlicmFyeSwgZXZlbiBtb3JlIHBvd2VyZnVsbC4gQnV0IHNvbWV0
...
But, I think needs more complicated structure;
<?php
class GRepo
{
protected
// needs "user"
$src_userRepos = "https://api.github.com/users/%s/repos",
// needs "user,repo"
$src_userRepoDetails = "https://api.github.com/repos/%s/%s",
$responseCode, $responseText,
$user;
public function __construct($user) {
$this->user = $user;
}
public function listRepos() {
$this->_request(
sprintf($this->src_userRepos, $this->user));
if ($this->responseCode != 200) {
throw new Exception('Server error!'); // e.g
}
return json_decode($this->responseText);
}
public function getRepoDetails($repo) {
$this->_request(
sprintf($this->src_userRepoDetails, $this->user, $repo));
if ($this->responseCode != 200) {
throw new Exception('Server error!'); // e.g
}
return json_decode($this->responseText);
}
// Could be extended, e.g with CURL..
protected function _request($url) {
$contents =# file_get_contents($url);
$this->responseCode = (false === $contents) ? 400 : 200;
$this->responseText = $contents;
}
}
// Test
$gr = new GRepo('qeremy');
print_r( $gr->listRepos() );
print_r( $gr->getRepoDetails('mii') );
?>
When you say "display a repo and its contents" you actually say "display the state of the repo after the latest commit of the master branch", right? That's actually the better way of thinking about the problem and will be a better guide through using GitHub's API.
You need to look at the Git data part of the API. Here's what you need to do:
1) fetch the list of refs for your repo using:
https://api.github.com/repos/:user/:repo/git/refs
Working example:
https://api.github.com/repos/izuzak/noam/git/refs
Notice that it lists the references in your repo and gives you links to continue.
2) fetch the commit object of the ref that interests you, namely "master", using the link provided in the response to 1):
https://api.github.com/repos/:user/:repo/git/commits/:sha
Working example:
https://api.github.com/repos/izuzak/noam/git/commits/5cf12775b844664d5f7af6663706195680181374
Notice that you get back an object with a link to a tree.
3) fetch the tree object of the last commit in the master ref, using the link provided in the response to 2) :
https://api.github.com/repos/:user/:repo/git/trees/:sha
Working example:
https://api.github.com/repos/izuzak/noam/git/trees/8a721bea8d2f281c87b39c74cbf5a70075d686b4
Notice that you get back a list of files in the root directory that is your repo. This is what you want. If you have subdirectories, you will get links to fetch the files in those subdirectories.
This should be enough to get you started :). Good luck!
Please try following library also available on git hub:
https://github.com/ornicar/php-github-api
You need to parse the respone Githubs API sends you back. In PHP you can do this by using json_decode() which will give you an array to work with. You can use something like curl to issue the requests from PHP and then get the results and parse them as described above.
Another way to do this are REST Client classes for PHP, have a look at this one here for example.
If you want some source code to analyze, about javascript you can try to start from GitHub Repositories (more specifically here), it's a nice open project for a Chrome extension which does something similiar to what you're looking for.
you can use github api
organization="write-here-the-organization"
githubuser="your-github-user"
token=`curl -i -u ${githubuser} -d '{"scopes": ["repo"]}' https://api.github.com/authorizations | grep token | cut -d\" -f 4`
curl -i -H "Authorization: token ${token}" https://api.github.com/orgs/${organization}/repos
as result of the above you will get a long json with all repositories, and their information. you can continue from here.
In PHP I would like to be able to access PUT and DELETE vars globally similar to how GET and POST vars are accessed globally. I originally considered adding the data to $_PUT and $_DELETE respectively in the global namespace, but then I realized that the data for each request is stored in the message body so there's no way for there to be more than one dataset from a POST, PUT, or DELETE request.
Are there any side-effects of overwriting the $_POST variable?
i.e. str_parse( file_get_contents( 'php://input' ), $_POST );
Am I being silly, or is there a better way to access PUT and DELETE data?
Edit to clarify my thinking:
I am very well aware of the source of the data in $_POST, in fact i mentioned it earlier in my question. If a HTTP POST request is sent to the server the data is stored in php://input. If a HTTP PUT or DELETE request is sent to the server, the data is stored in the exact same place, meaning that $_POST will be empty (as no data was POSTed despite data being available.
A GET request, on the other hand, is passed via the query string. This allows simultaneous passing of $_POST and $_GET variables. It is not possible to simultaneously pass POST and PUT or DELETE variables.
If I overwrite $_POST from php://input on PUT and or DELETE requests, there is no data loss.
The alternative of adding:
global $_PUT;
global $_DELETE;
to the beginning of functions seems silly, as I'll only be able to use one at a time anyway.
My first question, which is the one I really want answered, is about what side-effects or issues exist in overwriting $_POST. I can't possibly be the first person to try something as silly as:
$_POST['foo'] = 'bar';
I'm just concerned that if I do anything similar that it might not be preserved across scopes.
You'll see this called "bad practice" all over the internet, but if you really get in to why it is "bad practice", well, the answers get fuzzy. The most concrete reason is the "hit by a bus" scenario so often bandied about - what if the project gets handed off to a new developer?
Hand wringing aside (you can leave comments, after all), there really isn't a compelling reason not to do it like this, but again, there isn't a compelling reason to do it, either. Why not put the values in a $_SESSION key if you want them global? Or make a global variable? Or make a static class to access the PUT/DELETE values through? With all the other optional approaches, I think that overwriting $_POST, while it won't make your server explode, is the most likely to cause you a headache down the road.
I threw this little static class together, you'll want to test this out before relying on it. Use:
//To check if this is a rest request:
Rest::isRest();
//To get a parameter from PUT
$put_var = Rest::put('keyname', false);
//To get a parameter from DELETE
$dele_var = Rest::delete('keyname', false);
class Rest {
static private $put = false;
static private $delete = false;
static private $is_rest = false;
function __construct() {
self::$is_rest = true;
switch ($_SERVER['REQUEST_METHOD']) {
case 'PUT':
parse_str(self::getVars(), self::$put);
break;
case 'DELETE':
parse_str(self::getVars(), self::$delete);
break;
default:
self::$is_rest = false;
}
}
private static function getVars() {
if (strlen(trim($vars = file_get_contents('php://input'))) === 0)
$vars = false;
return $vars;
}
public static function delete($key=false, $default=false) {
if (self::$is_rest !== true)
return $default;
if (is_array(self::$delete) && array_key_exists($key, self::$delete))
return self::$delete[$key];
return $default;
}
public static function put($key=false, $default=false) {
if (self::$is_rest !== true)
return $default;
if (is_array(self::$put) && array_key_exists($key, self::$put))
return self::$put[$key];
return $default;
}
public static function isRest() {
return self::$is_rest;
}
}
Leave Post and Get as it is. it shouldn't be modified as it's for reading purposes only. Create $_PUT and $_DELETE globals:
// globals
$_DELETE = array ();
$_PUT = array ();
switch ( $_SERVER['REQUEST_METHOD'] ) {
case !strcasecmp($_SERVER['REQUEST_METHOD'],'DELETE'):
parse_str( file_get_contents( 'php://input' ), $_DELETE );
break;
case !strcasecmp($_SERVER['REQUEST_METHOD'],'PUT'):
parse_str( file_get_contents( 'php://input' ), $_PUT );
break;
}
Not tested but you should get the idea. I was in the search for a rest framework myself some weeks ago and decided to go with python. Recess (http://www.recessframework.org/) sounds promising though
You shouldn't modify $_POST directly as this represents values coming from the client. Consider it read-only, and do any modifications in user-defined variable.
As a follow up regarding accessing PUT and DELETE data, currently there is no superglobal built in to PHP to access this data directly. As the data is file data, which can be rather large, the usefulness and efficiency of reading the entire file contents in a typical assignment statement $variable = $_PUT['file']; is questionable. Instead, it should be read in chunks. As such, the usage is consistent with reading from any other file input resource.
More on PUT here:
http://php.net/manual/en/features.file-upload.put-method.php
If you create a "request" object, then regardless whether the request comes over HTTP, command line, or through an HTML5 web socket, you will have a uniform way to access request data. You can then make the request object accessible in the global scope, or pass it as an argument to the required functions or methods.
Ideally you would store data that is independent of the request in static or global variables, e.g. settings that are "static" regardless of the request, and data specific to the request in a local variable or object, that could be used by your business logic. If you had a web socket server, for example, it would be easier to handle multiple request objects in a single PHP process. Here is an example that might help:
$headers = getallheaders();
$query = parse_str($_SERVER['QUERY_STRING']);
$data = file_get_contents('php://input');
if(strpos($headers['Content-Type'],'application/x-www-form-urlencoded') !== false)
{
$data = parse_str($data);
}
elseif(strpos($headers['Content-Type'],'application/json') !== false)
{
$data = json_decode($data);
}
elseif(strpos($headers['Content-Type'],'application/soap+xml') !== false)
{
$data = // parse soap
}
elseif(strpos($headers['Content-Type'],'application/xml') !== false)
{
$data = // parse xml
}
// else ...
$request = new Request($_SERVER['REQUEST_METHOD'],$data,$query);
// example business logic
$method = $request->get_request_method();
$obj = new BlogPost();
if($method == 'GET')
{
$obj->id($request->get_query('id'));
$obj->load();
}
elseif($method == 'PUT')
{
$obj->id($request->get_query('id'));
$obj->title($request->get_data('title'));
$obj->body($request->get_data('body'));
$obj->save();
}
elseif($method == 'POST')
{
$obj->title($request->get_data('title'));
$obj->body($request->get_data('body'));
$obj->save();
}
elseif($method == 'DELETE')
{
$obj->id($request->get_query('id'));
$obj->wipe();
}
Regardless of whether it is a PUT, POST, PATCH, or DELETE, there is only one body of data in the HTTP request, so your application does not need a complex $request object. The request object can make your controller (if you are using MVC) very simple.
Been having major issues trying to solve this issue, I'll be happy to give a +500 bounty to someone who can help me get this work.
Basically, I'm trying to call this web service using Nusoap:
https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx?op=QueryCustomer
This is what I've got so far:
class Eway
{
var $username = 'test#eway.com.au';
var $pw = 'test123';
var $customerId = '87654321';
private function setHeaders($client)
{
$headers = <<<EOT
<eWAYHeader xmlns="http://www.eway.com.au/gateway/managedPayment">
<eWAYCustomerID>$this->customerId</eWAYCustomerID>
<Username>$this->username</Username>
<Password>$this->pw</Password>
</eWAYHeader>
EOT;
$client->setHeaders($headers);
return $client;
}
function getCustomer($ewayId = 9876543211000)
{
$url = 'https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx?WSDL';
$client = new Nusoap_client($url, true);
$this->setHeaders($client);
$args['QueryCustomer'] = array('managedCustomerID'=>$ewayId);
$result = $client->call('QueryCustomer', $args);
print_r($result);
}
}
When I run this code and do $eway->getCustomer() I get the following error:
Array
(
[faultcode] => soap:Client
[faultstring] => eWayCustomerID, Username and Password needs to be specified in the soap header.
)
What am I doing wrong?
If you could fix my class and give me working code which is able to do the QueryCustomer method using the test customer id and return its info, I'll be glad to give you +500 rep and my eternal gratitude. Obviously it'll be 48 hours before I can start the bounty, but I promise that I will do it.
I could be missing the point, but you never actually assign the returned object to $client:
function getCustomer($ewayId = 9876543211000)
{
$url = 'https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx?WSDL';
$client = new Nusoap_client($url, true);
$client = $this->setHeaders($client);
$args['QueryCustomer'] = array('managedCustomerID'=>$ewayId);
$result = $client->call('QueryCustomer', $args);
print_r($result);
}
You could also set $client as a class variable if desired or by sending the parameter as a reference.
Looking at the data, I do not know if this matters, but you are using var for your class variable declarations and then using private for the function. If you are using php5 I would stay away from the var:
private $username = 'test#eway.com.au';
private $pw = 'test123';
private $customerId = '87654321';
Use the private or public or protected (whichever your class requires) instead to keep consistency. I doubt this will solve your problem, just something to be conscious about.
Possible Solution
Ok, doing some digging of my own, figured this out, you need to encase the actual header you add in a SOAP:Header deal. I tested the below and it was working for me, so give it a try:
private function setHeaders($client)
{
$headers = <<<EOT
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" >
<SOAP:Header>
<eWAYHeader xmlns="http://www.eway.com.au/gateway/managedPayment">
<eWAYCustomerID>$this->customerId</eWAYCustomerID>
<Username>$this->username</Username>
<Password>$this->pw</Password>
</eWAYHeader>
</SOAP:Header>
EOT;
$client->setHeaders($headers);
return $client;
}
It did not return any errors. So yea, it seems that is the likely culprit. (Note I also implemented the $client = $this->setHeaders($client); I mentioned above as well.
And my Final Answer is:
Alright did a bit of digging and found something that works. Not saying it is right, but yea it works.
private function setHeaders($client)
{
$headers = <<<EOT
<eWAYHeader xmlns="https://www.eway.com.au/gateway/managedpayment">
<eWAYCustomerID>$this->customerId</eWAYCustomerID>
<Username>$this->username</Username>
<Password>$this->pw</Password>
</eWAYHeader>
EOT;
$client->setHeaders($headers);
return $client;
}
function getCustomer($ewayId = 123456789012)
{
$url = 'https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx?WSDL';
$client = new nusoap_client($url);
$client = $this->setHeaders($client);
$args['QueryCustomer'] = array('managedCustomerID'=>$ewayId);
$result = $client->call('QueryCustomer', $args, $namespace='https://www.eway.com.au/gateway/managedpayment', $soapAction='https://www.eway.com.au/gateway/managedpayment/QueryCustomer');
print_r($result);
//echo "\n{$client->request}\n"; // This echos out the response you are sending for debugging.
}
It seems the namespace and soapAction were the key ingredients. I found these using the link you originally posted: https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx?op=QueryCustomer
Basically, I just looked at that response, and then did some searching to figure out the soapAction, and then just messed with it until the request being sent matched the page you posted. It returns a failed login, but yea. That generally means something is working, and is probably due to the test data. But that gives you a baseline to go off of.
And the $client->request is a handy debugging tool for the future.
Update 5:
nusoap actually wraps the request with SOAP-ENV, like:
<SOAP-ENV:Header><eWAYHeader xmlns="https://www.eway.com.au/gateway/managedpayment">
<eWayCustomerID>87654321</eWayCustomerID>
<Username>test#eway.com.au</Username>
<Password>test123</Password>
</eWAYHeader></SOAP-ENV:Header>
While in the docs for EWay soap:Header must be used. I couldn't find a mention of the latter in nusoap headers.
Update 4:
This link has a good tip:
Got it. It was a case issue but not
there, and their PDF is incorrect.
For anyone that gets this in the
future, the PDF says:
<eWAYHeader
xmlns="http://www.eway.com.au/gateway/managedPayment">
It should be:
<eWAYHeader
xmlns="https://www.eway.com.au/gateway/managedpayment">
So this right here:
$client->setHeaders($headers);
The SoapClient class doesn't have that method. Instead, you can create a new SoapHeader.
private function setHeaders($client)
{
$headers = new stdClass;
$headers->eWAYCustomerID = $this->customerId;
$headers->Username = $this->username;
$headers->Password = $this->pw;
$ewayHeader = new SoapHeader(
"http://www.eway.com.au/gateway/managedPayment",
"eWAYHeader",
$headers
);
$client->__setSoapHeaders(array($ewayHeader));
return $client;
}
Edit: Alright, digging deeper:
private function prepHeaders()
{
return array(
'eWAYHeader' => array(
'eWAYCustomerID' => $this->customerId,
'Username' => $this->username,
'Password' => $this->pw
)
);
}
function getCustomer($ewayId = 9876543211000)
{
$url = 'https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx?WSDL';
$client = new nusoap_client($url);
$args['QueryCustomer'] = array('managedCustomerID'=>$ewayId);
$result = $client->call('QueryCustomer', $args, null, null, $this->prepHeaders());
print_r($result);
}
What happens if you do that?
I know this is not a full solution to the issue, but although this question is quite old, my findings may help lead to a concrete resolution.
I've been experiencing a similar error in relation to your mention of the HTTP "SOAPAction" header. (I am, however, dealing with a different eWay API than you. I'm dealing with the "Rapid API", which last week was renamed to from "Merchant Hosted Payments", which was part of the reason why my script wasn't working).
To return to the point, I found that if you don't specify the HTTP "SOAPAction" header, eWay returns a SoapFault with the following error message.
"System.Web.Services.Protocols.SoapException: Unable to handle request without a valid action parameter. Please supply a valid soap action."
If you add the HTTP "SOAPAction" header, you get an error no matter what you set it to.
"System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: XXX"
I'm also told by a member of eWay's support staff that they have an issues with an internal redirect, which they are now looking into resolving.
<ME> (2012-05-25 02:50:18)
I had an idea of what it could be. What is the "SOAPAction" HTTP header supposed to be set to?
<ME> (2012-05-25 02:52:05)
I couldn't find it in the documentation.
<EWAY_SUPPORT_STAFF> (2012-05-25 02:53:38)
The only thing that is required typically is the endpoint which is https://au.ewaypayments.com/hotpotato/soap.asmx and the <CreateAccessCode xmlns="https://au.ewaypayments.com/hotpotato/">
<EWAY_SUPPORT_STAFF> (2012-05-25 02:54:10)
In my tests it is working but what is happening is that requests are being redirected to the old URL which does not accept the CreateAccessCode method
<ME> (2012-05-25 02:56:58)
You did say that.
<ME> (2012-05-25 02:57:13)
So is this bug happening in the production environment?
<EWAY_SUPPORT_STAFF> (2012-05-25 02:57:57)
Yes it appears so. I have escalated this to Development and attached our last chat transcript and my own test results. They are looking at it now.
Can I get a HttpRequest automatically created from the environment? In other words, right now it seems like you have to...
$request = new HttpRequest;
$request->setCookies($_COOKIE);
$request->setHeaders(apache_request_headers());
$request->setPostFields($_POST);
$request->setQueryData($_GET);
$request->setRawPostData(file_get_contents('php://input'));
$request->setUrl($_SERVER['REQUEST_URI'']);
We also need to set the method -- a ridiculous chore, since $_SERVER['REQUEST_METHOD'] is a string and HttpRequest::setMethod takes an int in the HTTP_METH_* series of contants. So you have to set up your own mapping.
I want to like HttpRequest, but it seems cumbersome to use at the moment. I hope I'm missing something.
Edit:
The idea is to make testing cleaner. $_COOKIE and friends are superglobals. How do you test that?
function receiveRequest() {
$code = 'that touches superglobals like' . $_COOKIE['example'];
$response = new HttpResponse;
$response->setStatus(200);
return $response;
}
function testServer() {
$oldCookie = $_COOKIE;
$oldPost = $_POST;
// etc...
$_COOKIE = array('example' => 'stuff');
$_POST = array();
// etc...
$response = receiveRequest();
$_COOKIE = $oldCookie;
$_POST = $oldPost;
// etc...
assert($response->getStatus() === 200);
}
You need to control the state of not just what you use -- $_COOKIE in this example -- but every superglobal. There are about a dozen. It would be a lot cleaner to wrap up all that stuff in HttpRequest.
function receiveRequest(HttpRequest $request) {
$code = 'is purely a function of arguments like' . $request->getCookie('example');
$response = new HttpResponse;
$response->setStatus(200);
return $response;
}
function testServer() {
$request = new HttpRequest;
$request->setCookie('example' => 'stuff');
$response = receiveRequest($request);
assert($response->getStatus() === 200);
}
Then my actual server.php would use the hypothetical static method that I'm looking for.
$request = HttpRequest::generateRequestFromEnvironment($_COOKIE, $_POST, ...);
unset($_COOKIE, $_POST, ...);
$response = receiveRequest($request);
$response->send();
This seems to me like an odd use case - you want to create an HttpRequest to...yourself? Using exactly the parameters you were passed? Why?
The normal case is for requesting another resource, from another host, for which the kind of "automatic setup" you desire is pretty useless. If you really need this situation, it seems trivial to wrap this in a function that's easily reused?
From what you've posted, I think you're using HttpRequest as a container for all the information about the request which triggered your script.
This isn't what it is for - it is for making HTTP requests to other services from within your script. For example, you might request data from the Flickr API