Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • AbstractCommand
  • ClosureCommand
  • DefaultRequestSerializer
  • DefaultResponseParser
  • OperationCommand
  • OperationResponseParser

Interfaces

  • CommandInterface
  • RequestSerializerInterface
  • ResponseClassInterface
  • ResponseParserInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: namespace Guzzle\Service\Command;
  4: 
  5: use Guzzle\Common\Collection;
  6: use Guzzle\Common\Exception\InvalidArgumentException;
  7: use Guzzle\Http\Message\Response;
  8: use Guzzle\Http\Message\RequestInterface;
  9: use Guzzle\Http\Curl\CurlHandle;
 10: use Guzzle\Service\Client;
 11: use Guzzle\Service\ClientInterface;
 12: use Guzzle\Service\Description\Operation;
 13: use Guzzle\Service\Description\OperationInterface;
 14: use Guzzle\Service\Description\ValidatorInterface;
 15: use Guzzle\Service\Description\SchemaValidator;
 16: use Guzzle\Service\Exception\CommandException;
 17: use Guzzle\Service\Exception\ValidationException;
 18: 
 19: /**
 20:  * Command object to handle preparing and processing client requests and responses of the requests
 21:  */
 22: abstract class AbstractCommand extends Collection implements CommandInterface
 23: {
 24:     // Option used to specify custom headers to add to the generated request
 25:     const HEADERS_OPTION = 'command.headers';
 26:     // Option used to add an onComplete method to a command
 27:     const ON_COMPLETE = 'command.on_complete';
 28:     // Option used to disable any pre-sending command validation
 29:     const DISABLE_VALIDATION = 'command.disable_validation';
 30:     // Option used to override how a command result will be formatted
 31:     const RESPONSE_PROCESSING = 'command.response_processing';
 32:     // Different response types that commands can use
 33:     const TYPE_RAW = 'raw';
 34:     const TYPE_MODEL = 'model';
 35:     const TYPE_NO_TRANSLATION = 'no_translation';
 36:     // Option used to change the entity body used to store a response
 37:     const RESPONSE_BODY = 'command.response_body';
 38: 
 39:     /**
 40:      * @var ClientInterface Client object used to execute the command
 41:      */
 42:     protected $client;
 43: 
 44:     /**
 45:      * @var RequestInterface The request object associated with the command
 46:      */
 47:     protected $request;
 48: 
 49:     /**
 50:      * @var mixed The result of the command
 51:      */
 52:     protected $result;
 53: 
 54:     /**
 55:      * @var OperationInterface API information about the command
 56:      */
 57:     protected $operation;
 58: 
 59:     /**
 60:      * @var mixed callable
 61:      */
 62:     protected $onComplete;
 63: 
 64:     /**
 65:      * @var ValidatorInterface Validator used to prepare and validate properties against a JSON schema
 66:      */
 67:     protected $validator;
 68: 
 69:     /**
 70:      * Constructor
 71:      *
 72:      * @param array|Collection   $parameters Collection of parameters to set on the command
 73:      * @param OperationInterface $operation Command definition from description
 74:      */
 75:     public function __construct($parameters = null, OperationInterface $operation = null)
 76:     {
 77:         parent::__construct($parameters);
 78:         $this->operation = $operation ?: $this->createOperation();
 79:         foreach ($this->operation->getParams() as $name => $arg) {
 80:             $currentValue = $this->get($name);
 81:             $configValue = $arg->getValue($currentValue);
 82:             // If default or static values are set, then this should always be updated on the config object
 83:             if ($currentValue !== $configValue) {
 84:                 $this->set($name, $configValue);
 85:             }
 86:         }
 87: 
 88:         $headers = $this->get(self::HEADERS_OPTION);
 89:         if (!$headers instanceof Collection) {
 90:             $this->set(self::HEADERS_OPTION, new Collection((array) $headers));
 91:         }
 92: 
 93:         // You can set a command.on_complete option in your parameters to set an onComplete callback
 94:         if ($onComplete = $this->get('command.on_complete')) {
 95:             $this->remove('command.on_complete');
 96:             $this->setOnComplete($onComplete);
 97:         }
 98: 
 99:         $this->init();
100:     }
101: 
102:     /**
103:      * Custom clone behavior
104:      */
105:     public function __clone()
106:     {
107:         $this->request = null;
108:         $this->result = null;
109:     }
110: 
111:     /**
112:      * Execute the command in the same manner as calling a function
113:      *
114:      * @return mixed Returns the result of {@see AbstractCommand::execute}
115:      */
116:     public function __invoke()
117:     {
118:         return $this->execute();
119:     }
120: 
121:     /**
122:      * {@inheritdoc}
123:      */
124:     public function getName()
125:     {
126:         return $this->operation->getName();
127:     }
128: 
129:     /**
130:      * Get the API command information about the command
131:      *
132:      * @return OperationInterface
133:      */
134:     public function getOperation()
135:     {
136:         return $this->operation;
137:     }
138: 
139:     /**
140:      * {@inheritdoc}
141:      */
142:     public function setOnComplete($callable)
143:     {
144:         if (!is_callable($callable)) {
145:             throw new InvalidArgumentException('The onComplete function must be callable');
146:         }
147: 
148:         $this->onComplete = $callable;
149: 
150:         return $this;
151:     }
152: 
153:     /**
154:      * {@inheritdoc}
155:      */
156:     public function execute()
157:     {
158:         if (!$this->client) {
159:             throw new CommandException('A client must be associated with the command before it can be executed.');
160:         }
161: 
162:         return $this->client->execute($this);
163:     }
164: 
165:     /**
166:      * {@inheritdoc}
167:      */
168:     public function getClient()
169:     {
170:         return $this->client;
171:     }
172: 
173:     /**
174:      * {@inheritdoc}
175:      */
176:     public function setClient(ClientInterface $client)
177:     {
178:         $this->client = $client;
179: 
180:         return $this;
181:     }
182: 
183:     /**
184:      * {@inheritdoc}
185:      */
186:     public function getRequest()
187:     {
188:         if (!$this->request) {
189:             throw new CommandException('The command must be prepared before retrieving the request');
190:         }
191: 
192:         return $this->request;
193:     }
194: 
195:     /**
196:      * {@inheritdoc}
197:      */
198:     public function getResponse()
199:     {
200:         if (!$this->isExecuted()) {
201:             $this->execute();
202:         }
203: 
204:         return $this->request->getResponse();
205:     }
206: 
207:     /**
208:      * {@inheritdoc}
209:      */
210:     public function getResult()
211:     {
212:         if (!$this->isExecuted()) {
213:             $this->execute();
214:         }
215: 
216:         if (null === $this->result) {
217:             $this->process();
218:             // Call the onComplete method if one is set
219:             if ($this->onComplete) {
220:                 call_user_func($this->onComplete, $this);
221:             }
222:         }
223: 
224:         return $this->result;
225:     }
226: 
227:     /**
228:      * {@inheritdoc}
229:      */
230:     public function setResult($result)
231:     {
232:         $this->result = $result;
233: 
234:         return $this;
235:     }
236: 
237:     /**
238:      * {@inheritdoc}
239:      */
240:     public function isPrepared()
241:     {
242:         return $this->request !== null;
243:     }
244: 
245:     /**
246:      * {@inheritdoc}
247:      */
248:     public function isExecuted()
249:     {
250:         return $this->request !== null && $this->request->getState() == 'complete';
251:     }
252: 
253:     /**
254:      * {@inheritdoc}
255:      */
256:     public function prepare()
257:     {
258:         if (!$this->isPrepared()) {
259:             if (!$this->client) {
260:                 throw new CommandException('A client must be associated with the command before it can be prepared.');
261:             }
262: 
263:             // If no response processing value was specified, then attempt to use the highest level of processing
264:             if (!$this->hasKey(self::RESPONSE_PROCESSING)) {
265:                 $this->set(self::RESPONSE_PROCESSING, self::TYPE_MODEL);
266:             }
267: 
268:             // Notify subscribers of the client that the command is being prepared
269:             $this->client->dispatch('command.before_prepare', array('command' => $this));
270: 
271:             // Fail on missing required arguments, and change parameters via filters
272:             $this->validate();
273:             // Delegate to the subclass that implements the build method
274:             $this->build();
275: 
276:             // Add custom request headers set on the command
277:             if ($headers = $this->get(self::HEADERS_OPTION)) {
278:                 foreach ($headers as $key => $value) {
279:                     $this->request->setHeader($key, $value);
280:                 }
281:             }
282: 
283:             // Add any curl options to the request
284:             if ($options = $this->get(Client::CURL_OPTIONS)) {
285:                 $this->request->getCurlOptions()->merge(CurlHandle::parseCurlConfig($options));
286:             }
287: 
288:             // Set a custom response body
289:             if ($responseBody = $this->get(self::RESPONSE_BODY)) {
290:                 $this->request->setResponseBody($responseBody);
291:             }
292: 
293:             $this->client->dispatch('command.after_prepare', array('command' => $this));
294:         }
295: 
296:         return $this->request;
297:     }
298: 
299:     /**
300:      * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is
301:      * set, then the command will validate using the default {@see SchemaValidator}.
302:      *
303:      * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema
304:      *
305:      * @return self
306:      */
307:     public function setValidator(ValidatorInterface $validator)
308:     {
309:         $this->validator = $validator;
310: 
311:         return $this;
312:     }
313: 
314:     /**
315:      * {@inheritdoc}
316:      */
317:     public function getRequestHeaders()
318:     {
319:         return $this->get(self::HEADERS_OPTION);
320:     }
321: 
322:     /**
323:      * Initialize the command (hook that can be implemented in subclasses)
324:      */
325:     protected function init() {}
326: 
327:     /**
328:      * Create the request object that will carry out the command
329:      */
330:     abstract protected function build();
331: 
332:     /**
333:      * Hook used to create an operation for concrete commands that are not associated with a service description
334:      *
335:      * @return OperationInterface
336:      */
337:     protected function createOperation()
338:     {
339:         return new Operation(array('name' => get_class($this)));
340:     }
341: 
342:     /**
343:      * Create the result of the command after the request has been completed.
344:      * Override this method in subclasses to customize this behavior
345:      */
346:     protected function process()
347:     {
348:         $this->result = $this->get(self::RESPONSE_PROCESSING) != self::TYPE_RAW
349:             ? DefaultResponseParser::getInstance()->parse($this)
350:             : $this->request->getResponse();
351:     }
352: 
353:     /**
354:      * Validate and prepare the command based on the schema and rules defined by the command's Operation object
355:      *
356:      * @throws ValidationException when validation errors occur
357:      */
358:     protected function validate()
359:     {
360:         // Do not perform request validation/transformation if it is disable
361:         if ($this->get(self::DISABLE_VALIDATION)) {
362:             return;
363:         }
364: 
365:         $errors = array();
366:         $validator = $this->getValidator();
367:         foreach ($this->operation->getParams() as $name => $schema) {
368:             $value = $this->get($name);
369:             if (!$validator->validate($schema, $value)) {
370:                 $errors = array_merge($errors, $validator->getErrors());
371:             } elseif ($value !== $this->get($name)) {
372:                 // Update the config value if it changed and no validation errors were encountered
373:                 $this->data[$name] = $value;
374:             }
375:         }
376: 
377:         // Validate additional parameters
378:         if ($properties = $this->operation->getAdditionalParameters()) {
379:             foreach ($this->getAll() as $name => $value) {
380:                 // It's only additional if it isn't defined in the schema
381:                 if (!$this->operation->hasParam($name)) {
382:                     // Always set the name so that error messages are useful
383:                     $properties->setName($name);
384:                     if (!$validator->validate($properties, $value)) {
385:                         $errors = array_merge($errors, $validator->getErrors());
386:                     } elseif ($value !== $this->get($name)) {
387:                         $this->data[$name] = $value;
388:                     }
389:                 }
390:             }
391:         }
392: 
393:         if (!empty($errors)) {
394:             $e = new ValidationException('Validation errors: ' . implode("\n", $errors));
395:             $e->setErrors($errors);
396:             throw $e;
397:         }
398:     }
399: 
400:     /**
401:      * Get the validator used to prepare and validate properties. If no validator has been set on the command, then
402:      * the default {@see SchemaValidator} will be used.
403:      *
404:      * @return ValidatorInterface
405:      */
406:     protected function getValidator()
407:     {
408:         if (!$this->validator) {
409:             $this->validator = SchemaValidator::getInstance();
410:         }
411: 
412:         return $this->validator;
413:     }
414: }
415: 
php-coveralls API documentation generated by ApiGen 2.8.0