Open judgej opened 8 years ago
I do realise there are many legacy gateways that need to be supported and we don't have resources to completely rewrite them. But we also need to have an eye on where this all could go in the future.
Right now there is the Http\ClientInterface in Omnipay 3.0: https://github.com/thephpleague/omnipay/blob/2245aa7f850ae9e06be9283ace37d6c9ef070d24/src/Common/Http/ClientInterface.php
You can either make a request directly ($client->request($method, $uri, array $headers = [], $body = null)
) or send an existing PSR-7 request ($client->sendRequest(RequestInterface $psrRequest)
). The actual HTTP client (Guzzle for now), does not care either, as long as it's a valid PSR-7 request.
In this case, the Client is also a RequestFactory (which violates single responsibility in favor of laziness). So you can create a PSR-7 request, modify it (follwing the PSR RequestInterface methods) and then send it to the client, without needing to know the specific implementation.
The response for the ClientInterface requests are PSR-7 responses. So you gateway can use that. That directly replaces the the Guzzle3 response object. So you gateway can use the PSR-7 ResponseInterface to get the data from the response (also, without actual implementation knowledge).
So most of the times, you would rewrite the sendData()
method in your Omnipay requests.
public function sendData($data)
{
$httpRequest = $this->httpClient->post($this->getEndpoint(), null, http_build_query($data, '', '&'));
$httpRequest->getCurlOptions()->set(CURLOPT_SSLVERSION, 6); // CURL_SSLVERSION_TLSv1_2 for libcurl < 7.35
$httpResponse = $httpRequest->send();
return $this->createResponse($httpResponse->getBody());
}
Could be something like (skipping the Guzzle specific because that is not possible anymore)
public function sendData($data)
{
$httpRequest = $this->httpClient->createRequest('POST', $this->getEndpoint(), [], http_build_query($data, '', '&'));
$httpResponse = $$this->httpClient->sendRequest($httpRequest);
return $this->createResponse((string) $httpResponse->getBody());
}
Or without creating a seperate request
public function sendData($data)
{
$httpResponse = $this->httpClient->request('POST', $this->getEndpoint(), [], http_build_query($data, '', '&'));
return $this->createResponse((string) $httpResponse->getBody());
}
So yes, PSR-7 is something that touches the gateways, but depending on the gateway, the effects should be limited.
So TLDR:
Moving away from interfaces/implementations tied to Guzzle3, to our own HttpInterface + PSR-7 interfaces:
Guzzle\Http\ClientInterface -> League\Omnipay\Common\Http\ClientInterface
Guzzle\Http\Message\RequestInterface -> Psr\Http\Message\RequestInterface
Guzzle\Http\Message\Response -> Psr\Http\Message\ResponseInterface
Cool - thanks. I'll try writing an OmniPay wrapper for my package and see how it goes, but it looks like it could slip in quite well.
I think my $data
here would be a PSR-7 message to give to the httpClient
, so the construction of that (putting in the source data, endpoint etc.) would happen before sendData()
. Hopefully building this as an exercise will help to understand how it will work.
This will be relevant for us also: https://github.com/php-fig/fig-standards/pull/759 When those are accepted, we can split our HttpClient and RequestFactory.
Yes, it is certainly needed for PSR-7 to be really useful as a portable interface. Having to create specific factory wrappers for each implementation now is a pain. I suspect it will always be necessary, but should hopefully be a lot simpler if all the factories use a common set of methods.
TBH I'm surprised the big implementations did not create a separate "HTTP construction and parsing" library that could be shared. That's the difficult bit that would benefit from having one really good implementation with lots of eyes on, rather than dozens of different approaches strapped onto PSR-7 implementations. Maybe there is still room for such a package?
TBH I'm surprised the big implementations did not create a separate "HTTP construction and parsing" library that could be shared.
Hmm, isn't what zend-diactoros and guzzlehttp/psr7 are?
Kind of. The PSR-7 implementations do not do simple stuff such as parsing the body. You need to do that yourself, which means inspecting the header and doing json_decode()
and str_parse()
yourself, and risking missing out on other body formats that may be sent. That is all low-level HTTP stuff that you really don't want to get involved in - you want to ask a message, "give me the data", and simply get the data.
The non-PSR-7 implementations of Guzzle and Diacoros do all that, but the PSR-7 implementations do not. It is a level above HTTP, so that's understandable, but I feel some library to handle that would be good.
Similarly for the application to be able to say to a PSR-7 Request message, "here is a file - just send it and don't bother we with how", and not have to worry about all the intricate complications of headers, encoding, body parts, etc. would be great. Again, that is built into the non-PSR-7 parts of those clients, but not the PSR-7 parts, which is also great, but there is this layer kind of missing. That means it gets reinvented, over and over, in many applications.
@judgej Perhaps #368 would make it easier to make/rely on small interfaces, so you can be a bit more flexible with your gateways.
v3 is broken. All I am getting is an error: classes/Nyholm\Psr7\Request.php when calling Omnipay::create(); I have php-http/guzzle6-adapter, I don't know what else is required?
Are you following the readme and using composer? V3 is working in multiple projects correctly..
Yep, v2 was working perfectly, then I upgraded everything over to v3 and instant breakage occurs. I just removed everything and tried fresh...
remove league/omnipay omnipay/braintree omnipay/stripe
require league/omnipay:^3 omnipay/stripe:^3 omnipay/braintree:"3.0.x-dev"
moneyphp/money suggests installing ext-gmp (Calculate without integer limits) moneyphp/money suggests installing florianv/swap (Exchange rates library for PHP) moneyphp/money suggests installing psr/cache-implementation (Used for Currency caching) php-http/discovery suggests installing puli/composer-plugin (Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details.) php-http/message suggests installing zendframework/zend-diactoros (Used with Diactoros Factories) php-http/message suggests installing slim/slim (Used with Slim Framework PSR-7 implementation)
I'm trying to understand just how deeply PSR-7 goes (or will go) into the gateway drivers of OmniPay 3.0. Here is my use-case that will help to serve as a point of reference.
I have written a gateway package for a gateway. It will generate PSR-7
Request
messages to send to the gateway, will accept PSR-7Response
messages in reply to gateway requests, and will also takeServerRequest
messages to parse and handle 3DSecure.The package is given a factory so that it can create PSR-7
Request
messages. It does not otherwise care what the underlying PSR-7 implementation is. It does not get involved in the HTTP client; that is in the application domain and again, it does not care what you use.Now, how would this plug into OmniPay 3.0? Is there a
Request
factory I can wire up to the package? Would 3.0 be able to accept the PSR-7Request
messages directly and not have to rip them apart into arrays? Would it be able to give PSR-7Response
andServerRequest
messages directly to the package?What I'm trying to understand is whether PSR-7 is (or can be) central to the way the core OmniPay and its gateway drivers communicate, or whether PSR-7 is just something that OmniPay uses internally, while still talking to gateways only through its own data structures/setters/getters? Or is there potential for both approaches?