webservice-client.rst 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. ======================
  2. The web service client
  3. ======================
  4. The ``Guzzle\Service`` namespace contains various abstractions that help to make it easier to interact with a web
  5. service API, including commands, service descriptions, and resource iterators.
  6. In this chapter, we'll build a simple `Twitter API client <https://dev.twitter.com/docs/api/1.1>`_.
  7. Creating a client
  8. =================
  9. A class that extends from ``Guzzle\Service\Client`` or implements ``Guzzle\Service\ClientInterface`` must implement a
  10. ``factory()`` method in order to be used with a :doc:`service builder <using-the-service-builder>`.
  11. Factory method
  12. --------------
  13. You can use the ``factory()`` method of a client directly if you do not need a service builder.
  14. .. code-block:: php
  15. use mtdowling\TwitterClient;
  16. // Create a client and pass an array of configuration data
  17. $twitter = TwitterClient::factory(array(
  18. 'consumer_key' => '****',
  19. 'consumer_secret' => '****',
  20. 'token' => '****',
  21. 'token_secret' => '****'
  22. ));
  23. .. note::
  24. If you'd like to follow along, here's how to get your Twitter API credentials:
  25. 1. Visit https://dev.twitter.com/apps
  26. 2. Click on an application that you've created
  27. 3. Click on the "OAuth tool" tab
  28. 4. Copy all of the settings under "OAuth Settings"
  29. Implementing a factory method
  30. -----------------------------
  31. Creating a client and its factory method is pretty simple. You just need to implement ``Guzzle\Service\ClientInterface``
  32. or extend from ``Guzzle\Service\Client``.
  33. .. code-block:: php
  34. namespace mtdowling;
  35. use Guzzle\Common\Collection;
  36. use Guzzle\Plugin\Oauth\OauthPlugin;
  37. use Guzzle\Service\Client;
  38. use Guzzle\Service\Description\ServiceDescription;
  39. /**
  40. * A simple Twitter API client
  41. */
  42. class TwitterClient extends Client
  43. {
  44. public static function factory($config = array())
  45. {
  46. // Provide a hash of default client configuration options
  47. $default = array('base_url' => 'https://api.twitter.com/1.1');
  48. // The following values are required when creating the client
  49. $required = array(
  50. 'base_url',
  51. 'consumer_key',
  52. 'consumer_secret',
  53. 'token',
  54. 'token_secret'
  55. );
  56. // Merge in default settings and validate the config
  57. $config = Collection::fromConfig($config, $default, $required);
  58. // Create a new Twitter client
  59. $client = new self($config->get('base_url'), $config);
  60. // Ensure that the OauthPlugin is attached to the client
  61. $client->addSubscriber(new OauthPlugin($config->toArray()));
  62. return $client;
  63. }
  64. }
  65. Service Builder
  66. ---------------
  67. A service builder is used to easily create web service clients, provides a simple configuration driven approach to
  68. creating clients, and allows you to share configuration settings across multiple clients. You can find out more about
  69. Guzzle's service builder in :doc:`using-the-service-builder`.
  70. .. code-block:: php
  71. use Guzzle\Service\Builder\ServiceBuilder;
  72. // Create a service builder and provide client configuration data
  73. $builder = ServiceBuilder::factory('/path/to/client_config.json');
  74. // Get the client from the service builder by name
  75. $twitter = $builder->get('twitter');
  76. The above example assumes you have JSON data similar to the following stored in "/path/to/client_config.json":
  77. .. code-block:: json
  78. {
  79. "services": {
  80. "twitter": {
  81. "class": "mtdowling\\TwitterClient",
  82. "params": {
  83. "consumer_key": "****",
  84. "consumer_secret": "****",
  85. "token": "****",
  86. "token_secret": "****"
  87. }
  88. }
  89. }
  90. }
  91. .. note::
  92. A service builder becomes much more valuable when using multiple web service clients in a single application or
  93. if you need to utilize the same client with varying configuration settings (e.g. multiple accounts).
  94. Commands
  95. ========
  96. Commands are a concept in Guzzle that helps to hide the underlying implementation of an API by providing an easy to use
  97. parameter driven object for each action of an API. A command is responsible for accepting an array of configuration
  98. parameters, serializing an HTTP request, and parsing an HTTP response. Following the
  99. `command pattern <http://en.wikipedia.org/wiki/Command_pattern>`_, commands in Guzzle offer a greater level of
  100. flexibility when implementing and utilizing a web service client.
  101. Executing commands
  102. ------------------
  103. You must explicitly execute a command after creating a command using the ``getCommand()`` method. A command has an
  104. ``execute()`` method that may be called, or you can use the ``execute()`` method of a client object and pass in the
  105. command object. Calling either of these execute methods will return the result value of the command. The result value is
  106. the result of parsing the HTTP response with the ``process()`` method.
  107. .. code-block:: php
  108. // Get a command from the client and pass an array of parameters
  109. $command = $twitter->getCommand('getMentions', array(
  110. 'count' => 5
  111. ));
  112. // Other parameters can be set on the command after it is created
  113. $command['trim_user'] = false;
  114. // Execute the command using the command object.
  115. // The result value contains an array of JSON data from the response
  116. $result = $command->execute();
  117. // You can retrieve the result of the command later too
  118. $result = $command->getResult().
  119. Command object also contains methods that allow you to inspect the HTTP request and response that was utilized with
  120. the command.
  121. .. code-block:: php
  122. $request = $command->getRequest();
  123. $response = $command->getResponse();
  124. .. note::
  125. The format and notation used to retrieve commands from a client can be customized by injecting a custom command
  126. factory, ``Guzzle\Service\Command\Factory\FactoryInterface``, on the client using ``$client->setCommandFactory()``.
  127. Executing with magic methods
  128. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  129. When using method missing magic methods with a command, the command will be executed right away and the result of the
  130. command is returned.
  131. .. code-block:: php
  132. $jsonData = $twitter->getMentions(array(
  133. 'count' => 5,
  134. 'trim_user' => true
  135. ));
  136. Creating commands
  137. -----------------
  138. Commands are created using either the ``getCommand()`` method of a client or a magic missing method of a client. Using
  139. the ``getCommand()`` method allows you to create a command without executing it, allowing for customization of the
  140. command or the request serialized by the command.
  141. When a client attempts to create a command, it uses the client's ``Guzzle\Service\Command\Factory\FactoryInterface``.
  142. By default, Guzzle will utilize a command factory that first looks for a concrete class for a particular command
  143. (concrete commands) followed by a command defined by a service description (operation commands). We'll learn more about
  144. concrete commands and operation commands later in this chapter.
  145. .. code-block:: php
  146. // Get a command from the twitter client.
  147. $command = $twitter->getCommand('getMentions');
  148. $result = $command->execute();
  149. Unless you've skipped ahead, running the above code will throw an exception.
  150. PHP Fatal error: Uncaught exception 'Guzzle\Common\Exception\InvalidArgumentException' with message
  151. 'Command was not found matching getMentions'
  152. This exception was thrown because the "getMentions" command has not yet been implemented. Let's implement one now.
  153. Concrete commands
  154. ~~~~~~~~~~~~~~~~~
  155. Commands can be created in one of two ways: create a concrete command class that extends
  156. ``Guzzle\Service\Command\AbstractCommand`` or
  157. :doc:`create an OperationCommand based on a service description <guzzle-service-descriptions>`. The recommended
  158. approach is to use a service description to define your web service, but you can use concrete commands when custom
  159. logic must be implemented for marshaling or unmarshaling a HTTP message.
  160. Commands are the method in which you abstract away the underlying format of the requests that need to be sent to take
  161. action on a web service. Commands in Guzzle are meant to be built by executing a series of setter methods on a command
  162. object. Commands are only validated right before they are executed. A ``Guzzle\Service\Client`` object is responsible
  163. for executing commands. Commands created for your web service must implement
  164. ``Guzzle\Service\Command\CommandInterface``, but it's easier to extend the ``Guzzle\Service\Command\AbstractCommand``
  165. class, implement the ``build()`` method, and optionally implement the ``process()`` method.
  166. Serializing requests
  167. ^^^^^^^^^^^^^^^^^^^^
  168. The ``build()`` method of a command is responsible for using the arguments of the command to build and serialize a
  169. HTTP request and set the request on the ``$request`` property of the command object. This step is usually taken care of
  170. for you when using a service description driven command that uses the default
  171. ``Guzzle\Service\Command\OperationCommand``. You may wish to implement the process method yourself when you aren't
  172. using a service description or need to implement more complex request serialization.
  173. .. important::::
  174. When implementing a custom ``build()`` method, be sure to set the class property of ``$this->request`` to an
  175. instantiated and ready to send request.
  176. The following example shows how to implement the ``getMentions``
  177. `Twitter API <https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline>`_ method using a concrete command.
  178. .. code-block:: php
  179. namespace mtdowling\Twitter\Command;
  180. use Guzzle\Service\Command\AbstractCommand;
  181. class GetMentions extends AbstractCommand
  182. {
  183. protected function build()
  184. {
  185. // Create the request property of the command
  186. $this->request = $this->client->get('statuses/mentions_timeline.json');
  187. // Grab the query object of the request because we will use it for
  188. // serializing command parameters on the request
  189. $query = $this->request->getQuery();
  190. if ($this['count']) {
  191. $query->set('count', $this['count']);
  192. }
  193. if ($this['since_id']) {
  194. $query->set('since_id', $this['since_id']);
  195. }
  196. if ($this['max_id']) {
  197. $query->set('max_id', $this['max_id']);
  198. }
  199. if ($this['trim_user'] !== null) {
  200. $query->set('trim_user', $this['trim_user'] ? 'true' : 'false');
  201. }
  202. if ($this['contributor_details'] !== null) {
  203. $query->set('contributor_details', $this['contributor_details'] ? 'true' : 'false');
  204. }
  205. if ($this['include_entities'] !== null) {
  206. $query->set('include_entities', $this['include_entities'] ? 'true' : 'false');
  207. }
  208. }
  209. }
  210. By default, a client will attempt to find concrete command classes under the ``Command`` namespace of a client. First
  211. the client will attempt to find an exact match for the name of the command to the name of the command class. If an
  212. exact match is not found, the client will calculate a class name using inflection. This is calculated based on the
  213. folder hierarchy of a command and converting the CamelCased named commands into snake_case. Here are some examples on
  214. how the command names are calculated:
  215. #. ``Foo\Command\JarJar`` **->** jar_jar
  216. #. ``Foo\Command\Test`` **->** test
  217. #. ``Foo\Command\People\GetCurrentPerson`` **->** people.get_current_person
  218. Notice how any sub-namespace beneath ``Command`` is converted from ``\`` to ``.`` (a period). CamelCasing is converted
  219. to lowercased snake_casing (e.g. JarJar == jar_jar).
  220. Parsing responses
  221. ^^^^^^^^^^^^^^^^^
  222. The ``process()`` method of a command is responsible for converting an HTTP response into something more useful. For
  223. example, a service description operation that has specified a model object in the ``responseClass`` attribute of the
  224. operation will set a ``Guzzle\Service\Resource\Model`` object as the result of the command. This behavior can be
  225. completely modified as needed-- even if you are using operations and responseClass models. Simply implement a custom
  226. ``process()`` method that sets the ``$this->result`` class property to whatever you choose. You can reuse parts of the
  227. default Guzzle response parsing functionality or get inspiration from existing code by using
  228. ``Guzzle\Service\Command\OperationResponseParser`` and ``Guzzle\Service\Command\DefaultResponseParser`` classes.
  229. If you do not implement a custom ``process()`` method and are not using a service description, then Guzzle will attempt
  230. to guess how a response should be processed based on the Content-Type header of the response. Because the Twitter API
  231. sets a ``Content-Type: application/json`` header on this response, we do not need to implement any custom response
  232. parsing.
  233. Operation commands
  234. ~~~~~~~~~~~~~~~~~~
  235. Operation commands are commands in which the serialization of an HTTP request and the parsing of an HTTP response are
  236. driven by a Guzzle service description. Because request serialization, validation, and response parsing are
  237. described using a DSL, creating operation commands is a much faster process than writing concrete commands.
  238. Creating operation commands for our Twitter client can remove a great deal of redundancy from the previous concrete
  239. command, and allows for a deeper runtime introspection of the API. Here's an example service description we can use to
  240. create the Twitter API client:
  241. .. code-block:: json
  242. {
  243. "name": "Twitter",
  244. "apiVersion": "1.1",
  245. "baseUrl": "https://api.twitter.com/1.1",
  246. "description": "Twitter REST API client",
  247. "operations": {
  248. "GetMentions": {
  249. "httpMethod": "GET",
  250. "uri": "statuses/mentions_timeline.json",
  251. "summary": "Returns the 20 most recent mentions for the authenticating user.",
  252. "responseClass": "GetMentionsOutput",
  253. "parameters": {
  254. "count": {
  255. "description": "Specifies the number of tweets to try and retrieve",
  256. "type": "integer",
  257. "location": "query"
  258. },
  259. "since_id": {
  260. "description": "Returns results with an ID greater than the specified ID",
  261. "type": "integer",
  262. "location": "query"
  263. },
  264. "max_id": {
  265. "description": "Returns results with an ID less than or equal to the specified ID.",
  266. "type": "integer",
  267. "location": "query"
  268. },
  269. "trim_user": {
  270. "description": "Limits the amount of data returned for each user",
  271. "type": "boolean",
  272. "location": "query"
  273. },
  274. "contributor_details": {
  275. "description": "Adds more data to contributor elements",
  276. "type": "boolean",
  277. "location": "query"
  278. },
  279. "include_entities": {
  280. "description": "The entities node will be disincluded when set to false.",
  281. "type": "boolean",
  282. "location": "query"
  283. }
  284. }
  285. }
  286. },
  287. "models": {
  288. "GetMentionsOutput": {
  289. "type": "object",
  290. "additionalProperties": {
  291. "location": "json"
  292. }
  293. }
  294. }
  295. }
  296. If you're lazy, you can define the API in a less descriptive manner using ``additionalParameters``.
  297. ``additionalParameters`` define the serialization and validation rules of parameters that are not explicitly defined
  298. in a service description.
  299. .. code-block:: json
  300. {
  301. "name": "Twitter",
  302. "apiVersion": "1.1",
  303. "baseUrl": "https://api.twitter.com/1.1",
  304. "description": "Twitter REST API client",
  305. "operations": {
  306. "GetMentions": {
  307. "httpMethod": "GET",
  308. "uri": "statuses/mentions_timeline.json",
  309. "summary": "Returns the 20 most recent mentions for the authenticating user.",
  310. "responseClass": "GetMentionsOutput",
  311. "additionalParameters": {
  312. "location": "query"
  313. }
  314. }
  315. },
  316. "models": {
  317. "GetMentionsOutput": {
  318. "type": "object",
  319. "additionalProperties": {
  320. "location": "json"
  321. }
  322. }
  323. }
  324. }
  325. You should attach the service description to the client at the end of the client's factory method:
  326. .. code-block:: php
  327. // ...
  328. class TwitterClient extends Client
  329. {
  330. public static function factory($config = array())
  331. {
  332. // ... same code as before ...
  333. // Set the service description
  334. $client->setDescription(ServiceDescription::factory('path/to/twitter.json'));
  335. return $client;
  336. }
  337. }
  338. The client can now use operations defined in the service description instead of requiring you to create concrete
  339. command classes. Feel free to delete the concrete command class we created earlier.
  340. .. code-block:: php
  341. $jsonData = $twitter->getMentions(array(
  342. 'count' => 5,
  343. 'trim_user' => true
  344. ));
  345. Executing commands in parallel
  346. ------------------------------
  347. Much like HTTP requests, Guzzle allows you to send multiple commands in parallel. You can send commands in parallel by
  348. passing an array of command objects to a client's ``execute()`` method. The client will serialize each request and
  349. send them all in parallel. If an error is encountered during the transfer, then a
  350. ``Guzzle\Service\Exception\CommandTransferException`` is thrown, which allows you to retrieve a list of commands that
  351. succeeded and a list of commands that failed.
  352. .. code-block:: php
  353. use Guzzle\Service\Exception\CommandTransferException;
  354. $commands = array();
  355. $commands[] = $twitter->getCommand('getMentions');
  356. $commands[] = $twitter->getCommand('otherCommandName');
  357. // etc...
  358. try {
  359. $result = $client->execute($commands);
  360. foreach ($result as $command) {
  361. echo $command->getName() . ': ' . $command->getResponse()->getStatusCode() . "\n";
  362. }
  363. } catch (CommandTransferException $e) {
  364. // Get an array of the commands that succeeded
  365. foreach ($e->getSuccessfulCommands() as $command) {
  366. echo $command->getName() . " succeeded\n";
  367. }
  368. // Get an array of the commands that failed
  369. foreach ($e->getFailedCommands() as $command) {
  370. echo $command->getName() . " failed\n";
  371. }
  372. }
  373. .. note::
  374. All commands executed from a client using an array must originate from the same client.
  375. Special command options
  376. -----------------------
  377. Guzzle exposes several options that help to control how commands are validated, serialized, and parsed.
  378. Command options can be specified when creating a command or in the ``command.params`` parameter in the
  379. ``Guzzle\Service\Client``.
  380. =========================== ============================================================================================
  381. command.request_options Option used to add :ref:`Request options <request-options>` to the request created by a
  382. command
  383. command.hidden_params An array of the names of parameters ignored by the ``additionalParameters`` parameter schema
  384. command.disable_validation Set to true to disable JSON schema validation of the command's input parameters
  385. command.response_processing Determines how the default response parser will parse the command. One of "raw" no parsing,
  386. "model" (the default method used to parse commands using response models defined in service
  387. descriptions)
  388. command.headers (deprecated) Option used to specify custom headers. Use ``command.request_options`` instead
  389. command.on_complete (deprecated) Option used to add an onComplete method to a command. Use
  390. ``command.after_send`` event instead
  391. command.response_body (deprecated) Option used to change the entity body used to store a response.
  392. Use ``command.request_options`` instead
  393. =========================== ============================================================================================
  394. Advanced client configuration
  395. =============================
  396. Default command parameters
  397. --------------------------
  398. When creating a client object, you can specify default command parameters to pass into all commands. Any key value pair
  399. present in the ``command.params`` settings of a client will be added as default parameters to any command created
  400. by the client.
  401. .. code-block:: php
  402. $client = new Guzzle\Service\Client(array(
  403. 'command.params' => array(
  404. 'default_1' => 'foo',
  405. 'another' => 'bar'
  406. )
  407. ));
  408. Magic methods
  409. -------------
  410. Client objects will, by default, attempt to create and execute commands when a missing method is invoked on a client.
  411. This powerful concept applies to both concrete commands and operation commands powered by a service description. This
  412. makes it appear to the end user that you have defined actual methods on a client object, when in fact, the methods are
  413. invoked using PHP's magic ``__call`` method.
  414. The ``__call`` method uses the ``getCommand()`` method of a client, which uses the client's internal
  415. ``Guzzle\Service\Command\Factory\FactoryInterface`` object. The default command factory allows you to instantiate
  416. operations defined in a client's service description. The method in which a client determines which command to
  417. execute is defined as follows:
  418. 1. The client will first try to find a literal match for an operation in the service description.
  419. 2. If the literal match is not found, the client will try to uppercase the first character of the operation and find
  420. the match again.
  421. 3. If a match is still not found, the command factory will inflect the method name from CamelCase to snake_case and
  422. attempt to find a matching command.
  423. 4. If a command still does not match, an exception is thrown.
  424. .. code-block:: php
  425. // Use the magic method
  426. $result = $twitter->getMentions();
  427. // This is exactly the same as:
  428. $result = $twitter->getCommand('getMentions')->execute();
  429. You can disable magic methods on a client by passing ``false`` to the ``enableMagicMethod()`` method.
  430. Custom command factory
  431. ----------------------
  432. A client by default uses the ``Guzzle\Service\Command\Factory\CompositeFactory`` which allows multiple command
  433. factories to attempt to create a command by a certain name. The default CompositeFactory uses a ``ConcreteClassFactory``
  434. and a ``ServiceDescriptionFactory`` if a service description is specified on a client. You can specify a custom
  435. command factory if your client requires custom command creation logic using the ``setCommandFactory()`` method of
  436. a client.
  437. Custom resource Iterator factory
  438. --------------------------------
  439. Resource iterators can be retrieved from a client using the ``getIterator($name)`` method of a client. This method uses
  440. a client's internal ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object. A client by default uses a
  441. ``Guzzle\Service\Resource\ResourceIteratorClassFactory`` to attempt to find concrete classes that implement resource
  442. iterators. The default factory will first look for matching iterators in the ``Iterator`` subdirectory of the client
  443. followed by the ``Model`` subdirectory of a client. Use the ``setResourceIteratorFactory()`` method of a client to
  444. specify a custom resource iterator factory.
  445. Plugins and events
  446. ==================
  447. ``Guzzle\Service\Client`` exposes various events that allow you to hook in custom logic. A client object owns a
  448. ``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling
  449. ``$client->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or
  450. event subscribers (classes that listen to specific events of a dispatcher).
  451. .. _service-client-events:
  452. Events emitted from a Service Client
  453. ------------------------------------
  454. A ``Guzzle\Service\Client`` object emits the following events:
  455. +------------------------------+--------------------------------------------+------------------------------------------+
  456. | Event name | Description | Event data |
  457. +==============================+============================================+==========================================+
  458. | client.command.create | The client created a command object | * client: Client object |
  459. | | | * command: Command object |
  460. +------------------------------+--------------------------------------------+------------------------------------------+
  461. | command.before_prepare | Before a command is validated and built. | * command: Command being prepared |
  462. | | This is also before a request is created. | |
  463. +------------------------------+--------------------------------------------+------------------------------------------+
  464. | command.after_prepare | After a command instantiates and | * command: Command that was prepared |
  465. | | configures its request object. | |
  466. +------------------------------+--------------------------------------------+------------------------------------------+
  467. | command.before_send | The client is about to execute a prepared | * command: Command to execute |
  468. | | command | |
  469. +------------------------------+--------------------------------------------+------------------------------------------+
  470. | command.after_send | The client successfully completed | * command: The command that was executed |
  471. | | executing a command | |
  472. +------------------------------+--------------------------------------------+------------------------------------------+
  473. | command.parse_response | Called when ``responseType`` is ``class`` | * command: The command with a response |
  474. | | and the response is about to be parsed. | about to be parsed. |
  475. +------------------------------+--------------------------------------------+------------------------------------------+
  476. .. code-block:: php
  477. use Guzzle\Common\Event;
  478. use Guzzle\Service\Client;
  479. $client = new Client();
  480. // create an event listener that operates on request objects
  481. $client->getEventDispatcher()->addListener('command.after_prepare', function (Event $event) {
  482. $command = $event['command'];
  483. $request = $command->getRequest();
  484. // do something with request
  485. });
  486. .. code-block:: php
  487. use Guzzle\Common\Event;
  488. use Guzzle\Common\Client;
  489. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  490. class EventSubscriber implements EventSubscriberInterface
  491. {
  492. public static function getSubscribedEvents()
  493. {
  494. return array(
  495. 'client.command.create' => 'onCommandCreate',
  496. 'command.parse_response' => 'onParseResponse'
  497. );
  498. }
  499. public function onCommandCreate(Event $event)
  500. {
  501. $client = $event['client'];
  502. $command = $event['command'];
  503. // operate on client and command
  504. }
  505. public function onParseResponse(Event $event)
  506. {
  507. $command = $event['command'];
  508. // operate on the command
  509. }
  510. }
  511. $client = new Client();
  512. $client->addSubscriber(new EventSubscriber());