Set request options after initialisation in guzzle 6 - php

In Guzzle with version < 6 I used to set my authentication header on the fly after the initialisation of the client. I used setDefaultOption() for this.
$client = new Client(['base_url' => $url]);
$client->setDefaultOption('auth', [$username, $password]);
However this functionality seems to be deprecated in version 6. How would I go about this?
Note: the reason I need to do it this way is because I'm using guzzle for batch requests where some requests need different authentication parameters.

The best option for Guzzle 6+ is to recreate the Client. Guzzle's HTTP client is now immutable, so whenever you want to change something you should create a new object.
This doesn't mean that you have to recreate the whole object graph, HandlerStack and middlewares can stay the same:
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
$stack = HandlerStack::create();
$stack->push(Middleware::retry(/* ... */));
$stack->push(Middleware::log(/* ... */));
$client = new Client([
'handler' => $stack,
'base_url' => $url,
]);
// ...
$newClient = new Client([
'handler' => $stack,
'base_url' => $url,
'auth' => [$username, $password]
]);

You can send the 'auth' param, while constructing the client or sending the request.
$client = new Client(['base_url' => $url, 'auth' => ['username', 'password', 'digest']]);
OR
$client->get('/get', ['auth' => ['username', 'password', 'digest']]);
The other way is to rewrite the requestAsync method and add your custom logic in it. But there is no reason for that.

Related

GuzzleHttp concurrency and cookies

To test our PHP backend we use PHPUnit (v7.3.5) and the GuzzleHttp (v6.3.3) extension for the interface tests.
I'm new to all this stuff and played a little bit arround. I would like to send concurrent requests, but I have to use the cookie feature too.
The concurrency works perfectly and I'm successful with the cookies too. But if i combine both, the concurrency is lost.
My code so far:
// create session
$jar = new \GuzzleHttp\Cookie\CookieJar;
// create client
$client = new Client([
'base_uri' => 'http://localhost',
'cookies' => $jar,
]);
// login
$client->get("index.php", [
'form_params' => [
'usr' => 'myUserName',
'pwd' => '#myPass*'
],
]);
// fill up request array
$requests = new array(
new Request('GET', 'myPage1'),
new Request('GET', 'myPage2'),
new Request('GET', 'myPage3'),
new Request('GET', 'myPage4'),
new Request('GET', 'myPage5'),
new Request('GET', 'myPage6'),
...
new Request('GET', 'myPage100'),
);
// create pool
$pool = new Pool($client, $requests, [
'concurrency' => 5,
'fulfilled' => function ($response, $index) {...},
'rejected' => function ($reason, $index) {...}
]);
// wait until all request are sent
$promise = $pool->promise();
$promise->wait();
If I comment out // 'cookies' => $jar, the concurrency works perfectly.
Is it not possible to achieve both or do I miss something?
CLOSED
It turned it out that the problem wasn't the test itself.
I'm running into the session_start() lock on the server.
And of course without a session, there is no lock...that explain everything
You might need to run the test in separate process adding the directive in the comment of your test function:
/**
* #runInSeparateProcess
*/
public function testYourTestFunction()
{
// Test code here
}

What's the correct way to use Guzzle 6 to create pool of asynchronous json requests to send to API endpoints?

My objective is to use Guzzle 6 to create a pool of asynchronous requests that PUT json data. Then monitor each $promise success/failure.
For comparison to my POOL code example, the following single request to $client->request() converts the 3rd parameter to encoded json and then adds the Content-type:application/json.**
$client = new Client([
'base_uri' => BASE_URL . 'test/async/', // Base URI is used with relative requests
'timeout' => 0, // 0 no timeout for operations and watching Promises
]);
$response = $client->request('PUT', 'cool', ['json' => ['foo' => 'bar']]);
On the receiving API endpoint, I can read the json from the single request above by doing the following:
$json = file_get_contents('php://input');
$json = json_decode($json, true);
Using the concurrent requests example in the docs, for creating a Pool of asynchronous requests using new Request(), I hoped the same parameters (method, url endpoint, json flag) could be used, as in the single $client->request() example above. However, yield new Request() does not handle the 3rd json parameter like $client->request(). What is the correct Guzzle function to call from my Pool code to set json and content-type correctly? Or is there a better way to create a large pool of asynchronous requests and monitor their outcome?
POOL code example:
$this->asyncRequests = [
[
'endpoint' => 'cool'
],
[
'endpoint' => 'awesome'
],
[
'endpoint' => 'crazy'
],
[
'endpoint' => 'weird'
]
];
$client = new Client([
'base_uri' => BASE_URL, // Base URI is used with relative requests
'timeout' => 0 // 0 no timeout for operations and watching Promises
]);
$requests = function ($asyncRequests) {
$uri = BASE_URL . 'test/async/';
foreach ($asyncRequests as $key => $data) {
yield new Request('PUT', "{$uri}{$data['endpoint']}", ['json' => ['foo' => 'bar']]);
}
};
$pool = new Pool($client, $requests($this->asyncRequests), [
'concurrency' => 10,
'fulfilled' => function ($response, $index) {
$this->handleSuccessPromises($response, $index);
},
'rejected' => function ($reason, $index) {
$this->handleFailurePromises($reason, $index);
},
]);
$promise = $pool->promise(); // Initiate the transfers and create a promise
$promise->wait(); // Force the pool of requests to complete.
Hopefully, someone else will jump in and let me know if there is a more correct way to accomplish my objective, but after looking under the hood in Guzzle I realized new Request()'s 3rd parameter was looking for header information, and the 4th parameter was looking for a body. So the following code works using the Pool.:
foreach ($syncRequests as $key => $headers) {
yield new Request('PUT', "{$uri}{$headers['endpoint']}", ['Content-type' => 'application/json'], json_encode(['json' => ['nonce' => $headers['json']]]));
}
Also in docs for Psr7\Request
If you want full control, don't use the Request() object in your Pool. Instead, start the request yourself by having your pool's generator yielding a callable function which starts the request. That gives you total control of all options. Here is a correct code example:
https://stackoverflow.com/a/40622269/5562035

Default form_params for guzzle 6

Is there a way to globally add form_params to all requests with guzzle 6?
For example:
$client = new \GuzzleHttp\Client([
'global_form_params' => [ // This isn't a real parameter
'XDEBUG_SESSION_START' => '11845',
'user_token' => '12345abc',
]
]);
$client->post('/some/web/api', [
'form_params' => [
'some_parameter' => 'some value'
]
]);
In my ideal world, the post would have the result of array_merge-ing global_form_params and form_params:
[
'XDEBUG_SESSION_START' => '11845',
'user_token' => '12345abc',
'some_parameter' => 'some value',
]
I can see also wanting something like this for query or json
According to Creating a client you can set "any number of default request options" and on the GuzzleHttp\Client Source Code
$client = new Client['form_params' => [form values],]);
would apply your form_params to every request.
This could create issues with GET requests due to the Content-Type header being changed within Client::applyOptions. It would ultimately depend on server configuration.
If your intentions are to have the client make both GET and POST requests then you might be better served by moving the form_params into middleware. For example:
$stack->push(\GuzzleHttp\Middleware::mapRequest(function (RequestInterface $request) {
if ('POST' !== $request->getMethod()) {
// pass the request on through the middleware stack as-is
return $request;
}
// add the form-params to all post requests.
return new GuzzleHttp\Psr7\Request(
$request->getMethod(),
$request->getUri(),
$request->getHeaders() + ['Content-Type' => 'application/x-www-form-urlencoded'],
GuzzleHttp\Psr7\stream_for($request->getBody() . '&' . http_build_query($default_params_array)),
$request->getProtocolVersion()
);
});

Attaching OAuth1 to GuzzleHttp\Client

I'm trying to upgrade a class to use GuzzleHttp\Client to search tweets using twitter api. I'm having trouble attaching Oauth1. It worked fine with Guzzle3 and OAuthPlugin.
Here is the code block:
$client = new Client(['base_uri' => 'https://api.twitter.com']);
$auth = new Oauth1([
'consumer_key' => Config::get('twitter.consumer_key'),
'consumer_secret' => Config::get('twitter.consumer_secret'),
'token' => Config::get('twitter.token'),
'token_secret' => Config::get('twitter.token_secret')
]);
// Not sure if this is correct
$client->getEmitter()->attach($auth); // This is line 40 inside TwitterServiceProvider.php
I get the following error:
InvalidArgumentException in Client.php line 80: Magic request methods require a URI and optional options array
1. in Client.php line 80
2. at Client->__call('getEmitter', array()) in TwitterServiceProvider.php line 40
P.S So far, I have figured, I should be using https://github.com/guzzle/oauth-subscriber. However, no luck yet.
Resolved.
I couldn't get it to work using getEmitter. This may not be the right approach.
I switched over to guzzle/oauth-subscriber and it works now. There is a good example here: https://github.com/guzzle/oauth-subscriber
base_uri options should have a trailing slash.
New Code:
$stack = HandlerStack::create();
$auth = new Oauth1([
'consumer_key' => Config::get('twitter.consumer_key'),
'consumer_secret' => Config::get('twitter.consumer_secret'),
'token' => Config::get('twitter.token'),
'token_secret' => Config::get('twitter.token_secret')
]);
$stack->push($auth);
$client = new Client([
'base_uri' => 'https://api.twitter.com/1.1/',
'handler' => $stack,
'auth' => 'oauth'
]);
A request is made like below:
$client->get('search/tweets.json', [
'query' => ['q' => $query]
]);

How to get past login screen on Guzzle call

I have to send information to an external website using cURL. I set up Guzzle on my Laravel application. I have the basics set up, but according to the documentation of the website, there is an action that's required for the username and password. How can I pass the 'action' along with the credentials needed to log in and get access?
The website states:
curl [-k] –dump-header <header_file> -F “action=login” -F “username=<username>” -F “password=<password>” https://<website_URL>
My controller:
$client = new \GuzzleHttp\Client();
$response = $client->get('http://website.com/page/login/', array(
'auth' => array('username', 'password')
));
$xml = $response;
echo $xml;
The website will load on the echo, but it will only pull up the login screen. I need those credentials to bypass the login screen (with a successful login) to get to the portion of information I need for cURL.
curl -F submits a POST request instead of a GET request. So you'll need to modify your code accordingly, something like
$client = new \GuzzleHttp\Client();
$response = $client->post('http://website.com/page/login/', [
'body' => [
'username' => $username,
'password' => $password,
'action' => 'login'
],
'cookies' => true
]
);
$xml = $response;
echo $xml;
See http://guzzle.readthedocs.org/en/latest/quickstart.html#post-requests, http://curl.haxx.se/docs/manpage.html#-F
Edit:
Just add ['cookies' => true] to requests in order to use the auth cookie associated with this GuzzleHttp\Client(). http://guzzle.readthedocs.org/en/latest/clients.html#cookies
$response2 = $client->get('http://website.com/otherpage/', ['cookies' => true]);
I was having trouble getting #JeremiahWinsley's answer to work on newer version of Guzzle so I've updated their code to work as of Guzzle 5.x.
Three major changes are required
Using form_params instead of body to prevent the error "Passing in the "body" request option as an array to send a POST request has been deprecated."
Changing the cookies to use the CookieJar object
Use ->getBody()->getContents() to get the body of the request
Here is the updated code:
$client = new \GuzzleHttp\Client();
$cookieJar = new \GuzzleHttp\Cookie\CookieJar();
$response = $client->post('http://website.com/page/login/', [
'form_params' => [
'username' => $username,
'password' => $password,
'action' => 'login'
],
'cookies' => $cookieJar
]
);
$xml = $response->getBody()->getContents();
echo $xml;
And to continue using cookies in future requests, pass in the cookieJar to the request:
$response2 = $client->get('http://website.com/otherpage/', ['cookies' => $cookieJar]);
I was having trouble getting #JeremiahWinsley's and #Samsquanch's answer to work on newer version of Guzzle. So I've updated the code to work as of Guzzle 6.x.
Guzzle 6.x. documents: http://docs.guzzlephp.org/en/stable/index.html
Here is the updated code:
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
try {
$client = new Client();
$cookieJar = new CookieJar();
$response = $client->request('POST', 'http://website.com/page/login/', [
'form_params' => [
'username' => 'test#example.com',
'password' => '123456'
],
'cookies' => $cookieJar
]);
$response2 = $client->request('GET', 'http://website.com/otherpage/', [
'cookies' => $cookieJar
]);
if ($response2->getStatusCode() == 200) {
return $response2->getBody()->getContents();
} else {
return "Oops!";
}
} catch (\Exception $exception) {
return 'Caught exception: ', $exception->getMessage();
}

Categories