 Okay, can everybody hear me? No, yes, okay. Yes over there So today we're going to talk about guzzle. It's the HTTP client that is part of Drupal 8 core And there's a lot of technical content that I'm going to cover And it's going to focus primarily on guzzle itself, and then if we have time at the end I'll leave room for questions So first off my name is Michael Dowling But since we're at a conference and nobody knows people's names only Twitter handles. I'm mt. Dowling on Twitter I'm a software development engineer at Amazon web services That means that I program stuff and I work on the AWS SDK for PHP Which is a software development toolkit that allows PHP developers to connect to Amazon web services like easy to s3 in an idiomatic way But beyond just providing the simple Direct API one-to-one with each web service. We also provide abstractions over over them for example We offer a stream wrapper for Amazon s3 that you can just upload files s3 as if it were a file on disk So we offer some really cool stuff in the SDK. You should check it out And then when I'm not working I'm working on open-source projects like guzzle, which is why we're all here today So today we're going to cover these things We're going to talk about what guzzle has to do with Drupal, and we're going to cover the basics of guzzle itself I'm going to touch on version three of guzzle versus version four and why it's a big milestone for the project and And then at the end we'll cover how you can test guzzle clients effectively in a good way So first off what is Drupal and guzzle have to do with each other So if you didn't know this already Guzzle is the official HTTP client of Drupal 8. It's built into core If you download Drupal 8 then you're going to get guzzle source as well And it's already been implemented in several of the modules. A lot of those modules are to do with testing It's utilized by gout, which I believe is also used in your testing system And guzzle was added to Drupal to replace Drupal HTTP requests. Does anybody has anybody use Drupal HTTP requests? Yeah, everybody cool So apparently that's not the greatest function in the world and it's huge. It's really hard to test But don't take my word for it Boomba Tower a guy from the Drupal community. He opened an issue and he was talking about Drupal needs to get a better HTTP client and if Drupal is going to take itself seriously as a platform to build web Services on top of then it needs to take seriously the way in which you connect those web services So here's what Boomba Tower had to say Drupal's current outgoing HTTP capability is to be polite minimal We have one small function with a lousy API that can do basic requests But that's it if we want to be serious about web services. We need strong bi-directional HTTP support so what jumps out at me here is that Boomba Tower is mentioning that Drupal wants to be serious about connecting web services together Which is a trend that we're seeing more and more when you build your applications You're not just building against stuff that you build you probably have to connect with third-party APIs I already mentioned Amazon web services If you run an e-commerce you might have to download shopping feeds or upload your data to an ERP system so there's a lots of stuff that you need to interact with and Drupal's also, it's it's powerful itself as a as a web service framework But you need a powerful client to connect these frameworks together So With that said why didn't Drupal choose to use something built into PHP or why didn't Drupal choose to use a different library? But that also begs the question is why did I create guzzle in the first place? so I'll kind of enumerate the different options that we have and And talk about the pros and cons of those so first is file get contents and you probably all use this It's pretty awesome. It it allows you to register protocols You can even register custom stream wrappers that know how to work with different services like like the s3 stream rapper I talked about It's really great for quick things that you need to just get done and you don't need a lot of power It has a bit of a clumsy API So if you need to add custom headers or or you want to control timeout settings You have to create a stream context, and that's a PHP resource and then you add that whenever you use your file get contents call It's also quite underpowered. So it doesn't support persistent connections So if you need to send a request to the same web service over and over you're gonna pay us a an overhead for every request because that's a reconnect and Do the DNS resolution if that's not cash has to do all this stuff over and over you also can't send requests in parallel So file get contents. It's a sad face And then there's curl itself curl is amazing. It's probably the best HDB client that exists It can do almost anything that you'd want it to do. It works with tons of different protocols FTP SSH even HDP But the problem with curl is to use it effectively and to get the most out of it. You kind of have to be an expert at curl So if you want to use persistent connections and curl you need to reuse the same curl easy handle But if you reuse the same curl easy handle for just about every request You're gonna send then you can actually end up polluting that easy handle with the request options that can't be unset The reason this is is because like say you add a accepting coding header or an accepting coding option to a curl Easy handle that pollutes that handle to where you can't remove that value and future requests So then every request you would send from then on would have stuff associated with it that you don't want PHP 5.5 added a new feature called curl reset and this is awesome And it's utilizing guzzle for but it allows you to basically reuse an easy handle Get rid of everything on it except for the connection cache and the cookie cache. So that's great It's exactly what you need, but it's not available in PHP 5.4 earlier And then so you might say well, I can just use curl multi handles and throw away easy handles But if I use reuse the same multi handle, I know I'll have a connection cache because that manages the cache Yes, it does But that even you have to be an expert at know that you need to do selects and how to do those how to work with Windows how it's broken sometimes and you get a negative one return value. It's not documented There's a ton of stuff. You need to be an expert in a curl. It's awesome But you got to be an expert at it. So curl directly gets a sad face And there's peckle HTTP. It's a lot like curl. It's very powerful The biggest problem I have with it is that it's just not it's not built into PHP So if you're building a Library or something like that, you can't just rely that your users are going to have this available So because it's not as available as every other extension like curl as I'm going to give that a sad face, too And then there's duplication. So this is this is like the the yagney you're not going to need it so If if Drupal were to just say well, let's just remove Drupal HTTP requests and let developers do whatever they want to do You end up in a situation that I was in like five years ago whenever I first created guzzle so I worked at an e-commerce and We'd interact with a ton of different web services We had to interact with our ERP system to tell it like what orders we got that day So the people in the warehouse could pick and ship the products to the customers we had to interact with ad services all kinds of stuff and A lot of times what you'll find is these services they offer a PHP client, but it's not really the greatest thing in the world Usually the error handling isn't even thought of it might just trigger PHP errors out of nowhere They're usually not tested so I ended up just basically rewriting every client We ever had to interact with and I ended up rewriting the same thing over and over When you don't need to you can have a great base foundation framework and build on that Which is why guzzle was created so duplication and doing what what I used to do and creating your own coral wrappers over and over That gets a sad face, too So okay Well then I created a client on top of other stuff. Well, what about other user land clients that are available? Mike Carper from the Drupal community. He opened up another Drupal issue that got a ton of feedback on it and Mike did a comparison of a ton of different user land HTTP clients and He compared things like persistent connections parallel requests basically things that Mike needed himself But also that other common Drupal modules needed so he came up with this big graph here I don't you probably can't read it. That's not the point of the slide It's just to show that there's a lot of stuff that he looked at But they compared different libraries like buzz buzzy guzzle php multicurl hdp prl And they just went through a big list of of things that are they capable of this are they not and then we had references to everything so what ended up happening is I Guess guzzle had more yeses than everything else and anything that it didn't have a yes on originally I was able to help walk through the issue and add features as needed. So with all that The Drupal community chose to adopt guzzle as the HTTP client But Drupal isn't the only one using guzzle. It's also used in AWS SDK for PHP It's using gout. It's a common web scraper in PHP. You can use that to to scrape pages and iterate over HTML nodes as if they were CSS nodes. So like use CSS electors. It's really cool The biggest statistic here that I think is is Impressive is that over a thousand packages on packages have a direct dependency on guzzle and that's really awesome And it's been down over 1.7 million times So let's talk about guzzle itself now that we know the what I'll tell you how it's implemented and how it works guzzle is an HDB client and Apologies for the slide, but some people ask me like how can I use guzzle to build my back end and to make it an awesome? NBC framework. Well, you can't guzzle is the client. It's a lot like your web browser It sends requests to a server and it gets back a response over the internet. All right, that's out of the way So guzzle sends requests In Drupal they have this helper function. It's a static function on the Drupal class called HDB client and this will give you a basically like a global copy of the same HDB client Once you have that you can start sending requests with it. So here. We're doing a post request passing the URL and then oh, yeah I've got arrows So the HDB requests that gets sent from guzzle is going to look like this over the wire and you see it Serializes it for you. It does the headers. It adds a content link that based on the post requests It has the content type that you needed and it also And then finally you can get the body which you can echo it out as a string But when you're working with JSON services You can just call the JSON method of the response. This will handle JSON decoding the response body It'll implement error handling. So if an error occurs while you're parsing the JSON it'll throw an exception Then you can just access it using associative array syntax now response bodies Yeah, you can echo them because they implement two string But they're actually classes that you can you can interact with So here we're getting the body from a response object and then you can echo it cast it to a string Be careful with that because it actually loads it completely into memory and then prints it to standard out But you can also use tell Tell is from like, you know Linux f tell but it it tells you what position in the file you're at So a stream is a stream of data has bytes in it and as you read you you increment where you're at and your position in the Stream and tell tells you what your position is then it also can expose its capabilities to who are who's using the stream So it'll tell you if it's readable if it's writable or if it's seekable And you can use all these things together to interact with the stream objects as you need And then finally at the end it shows how you can loop over a stream object and read bytes as needed Rather than loading it all into memory Just use the EOF function and the read function but streams they help protect you from Loading all this data into memory by default it uses PHP's temp stream Abstraction so once the amount of data exceeds two megabytes in the stream it switches to a temporary file on disk So when you're sending requests through a client, there's all the HTTP methods are available on the client like put post delete everything you need But if the method that you you need to say it's a custom method and it's not listed there You can actually create a request object using the create request method of a client here we're creating a get request and then You can set headers on the on the request and build it up as needed So for example if you wanted to create a request and then pass it around to different collaborators that modify that request before it sent You can do that with a request object Set headers add headers You can remove headers and then finally when you're done building up your request object You just call send you pass the request object to the client send method and that will return your response object Cool, so I mentioned Guzzle for was released and it's a pretty big milestone for their project and Drupal actually is Using guzzle for now when they initially adopted guzzle it was on guzzle 3 But I address a few concerns that that y'all had like curl was a requirement I removed curl as a requirement now you can just implement your own stream adapter Not your stream your own HTTP adapter so that curls no longer required and then also has some other really cool features in it Like better performance. I went through basically every line of the code and Either rewrote it or updated it in some way to make it just architected better more testable It's simpler or removed a lot of features that that were either there for backwards compatibility Or that should be added on at a higher level and also now in guzzle for We use curl easy handles by default to use persistent connections if PHP 5.5 is available And if not it's still using curl multi, which is still powerful and everything But if you're using PHP 5.5 and you benefit from these architectural improvements It's about 40% faster when you're sending requests serially with guzzle for Swappable HTTP adapters are now possible so you can Swap out which adapter you use whether or not you want to use curl a stream stream adapter or implement your own by default guzzle uses curl, but Curls not always available. Well, it's one of the most ubiquitous HTTP clients in existence. It's like been downloaded over 500 million times it's not always on every system and Sometimes and I think this is a more important issue you run into version specific issues with curl where for example They had an issue when you used a SSL certificate authority and you reuse a curl easy handle You would get a memory leak So people need to be able to work around this and that's why I introduced swapable adapters And in curls not available or if you use the stream true request option We'll use the stream HTTP adapter that uses file get contents And then there's some opinionated stuff that I wrote about in a blog post I made whenever I released a release candidate I Feel like these give guzzle a better all-around implementation and make the library much easier to maintain And I'll kind of go over a few of those their opinions so you might not agree with all of them and Guzzle three all the implementation details were mostly protected methods and protected properties I Don't like that I think that it makes code hard to evolve and it's hard to maintain Because by making something protected you're basically making a statement that this is part of the public API of this class Because you could extend this class and you should you should be able to rely on this And if you can't rely on it that I need to make a version bump a major version bump because that would be a breaking change By changing these implementation details to private and only switching things to protected when I say This is what I've designed this class to be extended and how it's supposed to be used It makes the extension points much more explicit And it makes it easier for library maintainers to maintain these these classes because people rely on the API and you Focus on making sure the API can do everything it needs to do on Collaboration and composition rather than inheritance and it makes for a better product okay Then parallel requests in guzzle three were batches So that meant that say you had a hundred requests you wanted to send and you wanted to send them in chunks of 10 So you take 10 requests and send it to your batch And then when those 10 are done and you go get the next 10 and you send those and that's fine It works, but it introduced a lot of complexity when an exception was encountered in any of the batches it was Buffered and then thrown finally at the end of all the batches So then what you would get would be an aggregate exception class that I don't even know what I was thinking or how you would use it It was a bad API so now it has async rolling cues and that is a weird way of saying that as a Request completes a new request is added to the to the pool You basically you specify a pool size of say 10 and you seed it with 10 initial requests And then you start asynchronously sending those requests in parallel when an error occurs You're then expected to implement a listener that deals with the error asynchronously And exceptions are not thrown when you're in the parallel requests async mode And another one that's kind of controversial as I removed exception markers I don't think that these add value to PHP projects. Do y'all know what exception markers are? Okay, cool But they're basically like a library that Sorry Yes, okay, they're a library that you it's a library thing that you can do it's where all of your Exceptions extend from a base interface and then you can catch any exception thrown by that library and that sounds good in theory But it doesn't actually add any semantic value to the exceptions you're throwing So when you start adding a subclass for invalid argument exception or unexpected or bad method call things like these Those aren't things that you can you can recover from those are errors that just need to get bubbled up and and you need to handle That in your application layer And if you find yourself needing to catch every exception thrown by a specific library that you're utilizing in your application Then you're probably not using exceptions correctly either you need to when you call that that library when you call a method with it Try catch the exceptions of that that method is known to throw and then decorate or Throw your own custom exception that's specific to your application. So I got rid of exception markers So with all these changes, and I think it makes a great a much better product In the words of Beyonce Knowles, it's time to upgrade to guzzle for I think she said If you're creating a new project start with guzzle for don't use guzzle 3 Only use it if maybe you're stuck on PHP 5 3 And something Drupal did recently was adopt guzzle for which is really awesome So now let's talk about request options These are the things that you use to to create a request object from a client and it controls the behavior of a request controls How the response is downloaded things like that? The first we'll talk about some of the more common ones headers is a really common one. You just pass a hash of headers Here you're just setting some custom like ex foo header Then timeout so you can specify a timeout, which is the total time a transaction can take you can also specify a Connect timeout, which is only the connection time so DNS resolution connecting the sockets And these are specified in seconds. Oh Good question. I Don't remember But I think there is one. Yeah But If you wanted to specify your own default you can do that with the defaults value of a client When you create a client you can give it a hash of constructor options And one of those is defaults and there you can specify the hash of request options You want to apply to every request created by that client so say you're building up a client They need to interact with an API that you know uses basic off You know it goes through a proxy because it's on your own server And you can even set like query string values. It gets sent with every call So that's what the defaults value of a client allows Body is another request option. This allows you to upload data to the remote server for things like put and post Bodies can be a string. They can be a php stream So like f open or you can give it a guzzle stream Which is an abstraction that allows you to more flexibly work with streams of data Oh, yeah arrows again Okay, save to save two is just like body. It's the analogous form of what you do with a response body You can save the body of a response to a file on disk by providing a string So it'll open that file and it'll start pumping data into it or you can give it a f open or php Or a guzzle stream object Query is another common option that people use it Just adds a hash of query string values to the query string of the request that you're gonna send and guzzle will Take these nested query string values and serialize it in the way that php does with hdb build query You can also change out the serialization strategy using something called query aggregators, but we're not going to talk about that today Debug is a common option that you'll use if you run into issues connecting to an API If you open an issue on someone who builds a client on top of guzzle They'll probably ask you for this first thing is to give me the output of debug And what this does is it goes out to the adapter that you're utilizing and it'll dump out What that adapter did and the debug information from that adapter so like curls debug output Or if you're using the stream wrapper, it'll give you that debug output There's a ton of request options and I don't want to talk about all of them But you can view them on the online documentation And it's all implemented in message factory if you want to see a cool double dispatch implementation So now we're going to talk about the event system The event system in guzzle is basically how you build Robust applications and inject behavior to clients inject behaviors to requests at runtime it is It models the life cycle of a request so before you send it after you send it when it completes an error You can intercept requests In an event listener So if you want to stop a request from sending over the wire and using a different response you can do that and The event system in guzzle is kind of a fork of the symphony event dispatcher and the reason that is is because I Changed some stuff around to be more explicit with how you create listeners It's a bit faster because of this. I think it's a significantly faster because of this I move subscribe events from a static method of the of a class to a instance method which Allows me to actually utilize an event interface rather than having to rely on a concretion like the event class from symphony So this is why I did that in summary And then here's some vocabulary There's an emitter this is a thing that you register events with and it dispatches event objects to listeners then there's listeners listeners to get registered on an emitter and They listen to specific events by name with an event priority And they'll that event priority allows you to manage the order in which listeners are invoked and this is pretty important And then subscribers are a collection of listeners and they can encapsulate more complex behavior You can get an emitter from anything that implements the has emitter interface Clients and requests both implement that If you add a listener to a client then that listener or subscriber gets added to every request created by that client The the emitter of a client is basically a prototype that gets cloned and added to every request that's created So this is an example of both getting an emitter from a client and getting an emitter from a request So adding listeners you use the on method of an emitter once you get the emitter Just call on pass in the name of the event you want to listen to then pass in the function This is a callable in PHP and then an optional priority So in this example, we're we're registering an event on to a request and we're saying before the request is sent Print out the request using echo So listener priorities be deliberate about which priority you specify Because if you're doing a signer for a request you want to make sure that you're signing the request after it's been mutated and When it won't be changed again So like when you sign it if if you change the request after you sign it it might make that signature invalid So you need to be deliberate about where and when your events get registered and In order to make that easier. I noticed that it was a little bit hard to know like where should I put this event then Event priorities are just basically a number line So negative values come before positive values and it's just like a number line So negative 10 is going to get in both before 10 So use landmark priorities. These are priorities that have added to Guzzle HDP event request events. There's a bunch of constants there that you can use To kind of get an idea of where in guzzle itself. We've decided to use different priorities So on every event there are early and late priorities early means it's going to be emitted Really early in the vent lifecycle and late means it's really late The before event you can use prepare request This is what guzzle uses whenever it takes your request object and injects things like content type content length It determines whether or not your for HDP protocol version is correct So you can hook into that event if you need your event to fire before that happens You can subtract from these constant values if you needed to fire after you can add an arbitrary number to them after And then there's sign. This is an important one that if you add an event listener after the sign landmark and you modify or the request then you're at risk of Breaking people's signature implementations. So keep that in mind And then finally complete an error. They have these specific Constants verify a response. This is when guzzle says, okay. I got a response I was able to connect but is this a 200 or 300 level response and if it's not then it will throw and redirect response is when Right before guzzle is going to redirect a request event subscribers are basically a collection of event listeners and That allows you to create easy to distribute Behaviors and it allows you to make more complex behaviors in your application So if you were implementing an HDP cache for example, that has a lot of different events that you need to check on and that You need to implement handlers for so you would probably bundle that up into a subscriber Because you can't share a bunch of anonymous functions with people on the internet. That's why subscribers were invented Subscribers you have to implement a function get events. This is just returns a hash of the event name and then the callable you're going to invoke and This adds behavior to clients or or request at runtime You can add a subscriber to any emitter using the attach method and just passing the subscriber so here we're adding the history subscriber and So every time a request is sent by this this particular Client it will keep and maintain a history like a browser So there's life cycle events like I mentioned there's before which is before a request is sent over the wire There's headers which is you send the request over the wire and you're about to download the response body But you haven't done that yet Then there's complete which means you were able to get a response and error which means an error happened at any point in the request life cycle You can intercept the before event the complete event and the error event and this basically means that you stop the event chain at that particular moment and You intercept it with a response object So this is useful for for example cashing before you send a request over the wire You check to see do I have this cash if so intercept the before event with the cashed value So that way you're not going over the wire And When you intercept it stops event propagation further listeners won't get invoked after you've intercepted So this is an example of intercepting the before event Basically here you're checking it's a path of the request slash path or slash foo And if so then use this canned response object No need sending over the wire because I've got this and you intercept and then it Keeps the further event dispatchers the event listeners from firing then the adapters don't send the request over the wire And then notice also I decided to explicitly set a priority So completed requests you can intercept a complete event as well Well, that's actually no this isn't intercepting but you could intercept a complete event So say you got a response that you didn't like you could try the request again and inject a new response But this example is of logging slow requests. So this is a common thing that you might want to do First you get the emitter and you register with the complete event. I'll notice that the complete event It's actually a class. It's not like some raw bag of data with an arbitrary Number of keys in it that you have no idea what's there or how it works It's actually a class that you can get autocomplete on you know what methods it implements And it implements only the methods that were intended for that class how you're supposed to use it So it's a much easier system than what was found in guzzle 3 Little known fact probably my fault because it didn't document it was that you can get Transfer info from a completed event or a error event and this transfer information is passed From the adapter to the event and you can get things like how long it took to do the transaction How long it took to connect to the remote server things like that? So here we're getting that total amount of time and then at the total amount of time was greater than five seconds We'll send a message to the error log Error events work much in the same way as before complete This example is retrying a failed request three times. This is a failed connection three times First you get the you add an emitter You add a listener using the emitter on the error event and notice it's an error event class and Then error events can happen when you failed to connect to a remote server web server or if you Get a connection error. So one of these two things can happen if you get a connection error You're not going to have a response object available So you need to make sure that when you implement these things that you're you're doing so in a way that you know I need a response or I don't need a response here. So here we want to retry connection errors So we check is there a response if so bail out and let the further event listeners do their magic So That's what happened there. I just said that okay, so tries so Request objects have a bag of data on them that you can you can use as necessary To implement custom behavior and to give some kind of state to requests relative to that your custom listeners It's called the config object and this is just a key value pair hash that you can use to add arbitrary keys to it Here we're adding a retries key and we're grabbing that from the request and each time the request is needing to be retried we increment the tries and if the number of retries is less than or equal to three which is our threshold then we're going to actually Get the client from the event using the get client method of that event That's the same client that was used to send the original request and we'll call send again with the same request object And then once that response comes back will intercept the original event with a new response So this is a way that you can bubble up these new responses into the original event So that's kind of what I call an event loop in in guzzle So the normal event is event loop is you submit before you emit headers and then you emit complete But if at any time an error occurs even in the before of it So say that you have an error and exceptions thrown while you're preparing a request Well, then that gets caught and then it invokes the error listener So anytime you register an error listener, you're going to get invoked if there's an error at any point That can also happen on the complete event So if you've emitted complete and an error occurs while you're trying to I don't know persist it to some cash or something Maybe that throws an exception. That's going to also trigger the error event And this is where it gets a little more complicated where the loop thing comes in Say you emit the before then the headers then the error event and then in the error event You say okay, I can retry this so that starts a whole new Event lifecycle loop that emits before then headers and complete and if you get back a valid response from that You can then intercept the original event and then that would then once you've intercepted that event after the error Was triggered it will trigger the complete event. So it's sort of this loop that can occur But all those things that I just talked about there You don't really need to implement them because most of the stuff We just went over are already implemented and built-in event subscribers to either built in or modules that you can add on by just Using like a packages package So built in is the mock subscriber So allows you to create a queue of mock responses that get that intercept or request gets sent over the wire So you can use it for testing history. It's like a browser history redirects are implemented in guzzles subscribers rather than being like rather than relying on a Adapter to implement redirects is actually implemented with subscribers And cookies are also implemented and you should also note that in order to utilize cookies You have to actually turn cookies on per request Most web services don't really use cookies these days. So that's why I decided to do that External subscribers that you can utilize there's OAuth, which allows you to integrate with services like Twitter There's the log subscriber. So we went over that like logging slow requests There's actually a log subscriber that you can use to log requests and responses that are sent over the wire to a PSR 3 interface There's a retry subscriber that you can build up really complex retry logic and You can say I want to retry 500 and 503 errors Six times. I want to retry connection errors two times and you can build up any kind of custom logic there Then there's a transfer progress You can use that to for example create like a CLI progress bar if you're doing something of the command line And there's a fully working HTTP cache subscriber. This is still kind of work in progress. It works mostly right now I'm finishing up some last minute changes on it, but it implements HTTP 1.1 caching. So like Revalidate revalidation things like that. It can greatly speed up your application if you know that you're using valid cache headers Or that the web service you're consuming uses valid cache headers And then there's a message integrity subscriber So if your web service returns back something like a content md5 header You can use that header to validate that the response body you downloaded wasn't corrupted by like a misbehaving proxy Or someone eavesdropping on your request. So the message integrity is used for validation of responses And also you can create your own check sums when you're sending requests as well So here's a cool example of consuming Twitter With the OAuth plugin First you create a client and because we know we know we're going to use this client over and over to send requests to Twitter's API We're going to set up some defaults. I forgot to put the defaults hash there. I'll fix that later So we give it a base URL. I didn't mention this earlier But a base URL is just like how it works in a browser when you're building HTML webpages If you make an anchor tag that's relative say like slash path or no slash and then just your path Then that gets combined with the base URL to create a full URI Guzzle implements the same exact behavior that follows the same RFC that you would see in your browsers So that makes it easy to be able to specify relative paths and not have to know a full URL We can also set off so the OAuth subscriber requires that you set off on your request And then if it sees oh you set off to OAuth then it will sign it using OAuth Next you create the OAuth subscriber Here we're just passing in our API credentials like consumer key and things like that And then finally attach it to the client's emitter Which remember the client's emitter is a prototype that gets added to every request created by it So then you can just call get on the client and this is going to go send out to this relative URL It's going to combine it with the base URL and give you back the response object That you can then use like to say that get the JSON method to easily interact with the JSON response So when you test clients or really when you test anything You should follow these these these rules and it's probably not comprehensive. This is specific to clients Good tests are fast developers don't want to run tests that take forever to complete because they take forever Good tests are predictable if you rely on a third party It's website or web service to be available when you're writing your unit tests Then those unit tests could fail which then you have to go through and manually figure out Why did it fail so that makes it slow so that means no one's going to run them That means that your your site could break because you're not running your tests Good tests have no external dependencies because of what I just said and they work on a plane because developers like to write on a plane I did on the way here What that means what it boils down to is that good tests don't have access to the network Don't send hdb requests over the internet in your unit tests So how do you do that good tests use mocks use PHP unit use mockery whatever But inject your clients into collaborators don't have a collaborator Collaborator create the client because at that point you can't mock it you can't add emitters to the client to say I mean listeners to add mocks at runtime So good tests use the mock subscriber Good tests use the mock adapter and use this history subscriber and we'll cover all these just in just a second So using mocks use dependency injection inject your clients don't have Don't have your club them creating clients inside your constructors for example And you can use PHP unit mockery just like you normally do just mock out the methods that you're sending So if you know you're sending a get request Use the PHP unit Get mock builder and then mock that specific method have it return the can response that you want So the mock adapter When you create a client you can actually specify which adapter you use if you don't want to rely on guzzle using You know the most appropriate adapter for your system you can specify which adapter you want to use here we're giving it the mock adapter and this allows you to Basically every request that sent through this adapter you get a transaction object and that object is the it's a layer of indirection Between requests and responses. It's the only point in the library where request requests know about responses and responses know about requests and at this point you can All you need to do is return a response object So you can use a transaction or you can say Outside of this class bind it to a closure that knows exactly what to do, but this makes testing really easy And it allows you to test the entire flow of a request Rather than relying on the event system You can rely on the adapters to know all my events were sent nothing was intercepted all my adapters Whether or not I've decorated them or not we're all invoked up to this point So it allows you to be very explicit about what point you're testing The mock subscriber This is we talked about this earlier, but it's a queue of responses that you add to a Subscriber as requests are sent the mock subscriber looks at the queue Dequeues off the top and then inject intercepts that event with that queued response This is great for basically saying I know I'm gonna get This can response followed by this can response followed by this one So you can build up these queues and inject those into your listeners You can also mock exceptions. So say you want to ensure that you're handling errors Gracefully, you can add request exceptions to a mock subscriber So this example just builds up the mock subscriber Just call it's in the guzzle HDP subscriber mock namespace create a new mock You can add responses to it using a string so you can build up the raw HDP message Or you can give it a response object Excuse me, you can add an exception to it you can Then then once you've added your Request and response your response and your exception attach it to the emitter and then when you send your first request to slash foo You can get the status code. That was the 200 response that we queued up Oh the next one we send to slash foo We're gonna dequeue that exception and it throws the exception that you queued up So you can use this to build up scenarios in your tests to know exactly that you're you're working with it correctly And then finally a way in which you can know that you sent the request you wanted to send Rather than that you got the response you wanted to get the history subscriber allows you just to inspect every request You've sent through that client So create the history subscriber using new history Then attach it to the emitter Then once you call all of your your when you invoke all your requests It actually creates basically a ledger into the history subscriber that you can then use You can count the history subscriber You can get the last request that was sent through the history subscriber or the last response You can enumerate over all the requests and inspect specifically did I set the right header? Did I use OAuth? Did I send it to this specific path? So it's really handy to combine history with the mock subscriber and the mock adapter to to unit test effectively Don't send requests over the internet. Yeah So I didn't create a client in this example Yeah, but it's just assuming that you already have one created and then I oh, yeah I put mock instead of history, but I'll fix that later. Good catch So there's some other projects that are Written on top of guzzle the HDB client that we just talked about so the HDP portions of guzzle are part of Drupal core But there's other things that are built on top of it like guzzle commands Which allows you to encapsulate operations on a web service using basically a hash of input And you get back a hash of output it implements it its own sort of request command lifecycle and you can use that to build on top of To so you say you wanted to use swagger service descriptions at runtime you could create a command implementation that utilizes that But so in guzzle 3 there was a thing called guzzle services I don't know if anybody ever used that but it's a handy way to describe an API using a JSON document You say what methods it's gonna send what the response should be and then how you serialize it over the wire How you parse that response over the wire So guzzle services is an implementation on top of guzzle commands that you can use today It's still in beta the 4.0 version, but it's it's very much usable So in summary we talked about why Drupal and guzzle are working together Gave you the basics of guzzle the event system and how to effectively test guzzle clients so I hope that gave you everything you needed to know about guzzle gave you the tour and Now I want to open it up for questions if anybody has any yes There is a microphone if you want to use it, but I can repeat your question if you don't want to get up Right the question was am I going to take my fork that I made of the event dispatcher and try to merge it back in upstream No, because it changed the API of the event of Of the event dispatcher from symphony completely It uses different methods it has stricter method signatures So because of that, I mean I might I could raise an issue or something like that But they wouldn't change it or merge it in for the 2.0 branch Maybe in a 3 of symphony 3, but I don't know so as it right now. No, it's it's a guzzle emitter right now any other questions Yes, my name is Brian Hirsch from the web team at the White House first. Thank you very much for all your work on this I I rewrote the AWS SQS module proof of concept built on your latest PHP SDK For the 1.0 release had a great experience and then we also use guzzle for the tweet server We built for State of the Union awesome experience again to scratching the surface. So thank you We are interested in building some you know sort of interactive tools with an API that we've been building out You know like the word nick API documentation or swagger. I assume lots of people using guzzle want to build these sorts of things And use guzzle under the hood. Do you know projects that are doing this sort of thing? So we're not reinventing the wheel if we want to write a module for this great question So I mentioned guzzle services So that's guzzle's web service description format that uses its own custom JSON document similar to swagger It's not exactly one-to-one But the way that that was implemented was on top of the guzzle commands repository Which gives you the foundation that you need to build these types of systems So if you looked at the guzzle services repository and github you could see exactly how you're supposed to implement this It's basically an event system So before a command gets sent you're you're expected to serialize a request and then inject that into the command And then when a command has completed when the response was received You're expected to implement a listener that then parses a response using the description format that you're going to utilize and then gives back Whatever object your abstractions supposed to give so exactly that's what guzzle services does it's built on top of guzzle command They're both fairly stable and we're working on an upgrade to the AWS SDK that's going to utilize them as well So once that's done, I'm sure I'll tag like a stable release of that But check those out. It'll help out a lot. I think and those are running on guzzle three or four version four. Okay, great. Thanks a lot Any other questions? All right, cool. Thank you so much for coming