diff options
Diffstat (limited to 'vendor/guzzle/guzzle/src/Guzzle/Service/Command')
36 files changed, 2683 insertions, 0 deletions
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php new file mode 100644 index 0000000..e42ff90 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php @@ -0,0 +1,390 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Common\Collection; +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Curl\CurlHandle; +use Guzzle\Service\Client; +use Guzzle\Service\ClientInterface; +use Guzzle\Service\Description\Operation; +use Guzzle\Service\Description\OperationInterface; +use Guzzle\Service\Description\ValidatorInterface; +use Guzzle\Service\Description\SchemaValidator; +use Guzzle\Service\Exception\CommandException; +use Guzzle\Service\Exception\ValidationException; + +/** + * Command object to handle preparing and processing client requests and responses of the requests + */ +abstract class AbstractCommand extends Collection implements CommandInterface +{ + // @deprecated: Option used to specify custom headers to add to the generated request + const HEADERS_OPTION = 'command.headers'; + // @deprecated: Option used to add an onComplete method to a command + const ON_COMPLETE = 'command.on_complete'; + // @deprecated: Option used to change the entity body used to store a response + const RESPONSE_BODY = 'command.response_body'; + + // Option used to add request options to the request created by a command + const REQUEST_OPTIONS = 'command.request_options'; + // command values to not count as additionalParameters + const HIDDEN_PARAMS = 'command.hidden_params'; + // Option used to disable any pre-sending command validation + const DISABLE_VALIDATION = 'command.disable_validation'; + // Option used to override how a command result will be formatted + const RESPONSE_PROCESSING = 'command.response_processing'; + // Different response types that commands can use + const TYPE_RAW = 'raw'; + const TYPE_MODEL = 'model'; + const TYPE_NO_TRANSLATION = 'no_translation'; + + /** @var ClientInterface Client object used to execute the command */ + protected $client; + + /** @var RequestInterface The request object associated with the command */ + protected $request; + + /** @var mixed The result of the command */ + protected $result; + + /** @var OperationInterface API information about the command */ + protected $operation; + + /** @var mixed callable */ + protected $onComplete; + + /** @var ValidatorInterface Validator used to prepare and validate properties against a JSON schema */ + protected $validator; + + /** + * @param array|Collection $parameters Collection of parameters to set on the command + * @param OperationInterface $operation Command definition from description + */ + public function __construct($parameters = array(), OperationInterface $operation = null) + { + parent::__construct($parameters); + $this->operation = $operation ?: $this->createOperation(); + foreach ($this->operation->getParams() as $name => $arg) { + $currentValue = $this[$name]; + $configValue = $arg->getValue($currentValue); + // If default or static values are set, then this should always be updated on the config object + if ($currentValue !== $configValue) { + $this[$name] = $configValue; + } + } + + $headers = $this[self::HEADERS_OPTION]; + if (!$headers instanceof Collection) { + $this[self::HEADERS_OPTION] = new Collection((array) $headers); + } + + // You can set a command.on_complete option in your parameters to set an onComplete callback + if ($onComplete = $this['command.on_complete']) { + unset($this['command.on_complete']); + $this->setOnComplete($onComplete); + } + + // Set the hidden additional parameters + if (!$this[self::HIDDEN_PARAMS]) { + $this[self::HIDDEN_PARAMS] = array( + self::HEADERS_OPTION, + self::RESPONSE_PROCESSING, + self::HIDDEN_PARAMS, + self::REQUEST_OPTIONS + ); + } + + $this->init(); + } + + /** + * Custom clone behavior + */ + public function __clone() + { + $this->request = null; + $this->result = null; + } + + /** + * Execute the command in the same manner as calling a function + * + * @return mixed Returns the result of {@see AbstractCommand::execute} + */ + public function __invoke() + { + return $this->execute(); + } + + public function getName() + { + return $this->operation->getName(); + } + + /** + * Get the API command information about the command + * + * @return OperationInterface + */ + public function getOperation() + { + return $this->operation; + } + + public function setOnComplete($callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('The onComplete function must be callable'); + } + + $this->onComplete = $callable; + + return $this; + } + + public function execute() + { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be executed.'); + } + + return $this->client->execute($this); + } + + public function getClient() + { + return $this->client; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getRequest() + { + if (!$this->request) { + throw new CommandException('The command must be prepared before retrieving the request'); + } + + return $this->request; + } + + public function getResponse() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + return $this->request->getResponse(); + } + + public function getResult() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + if (null === $this->result) { + $this->process(); + // Call the onComplete method if one is set + if ($this->onComplete) { + call_user_func($this->onComplete, $this); + } + } + + return $this->result; + } + + public function setResult($result) + { + $this->result = $result; + + return $this; + } + + public function isPrepared() + { + return $this->request !== null; + } + + public function isExecuted() + { + return $this->request !== null && $this->request->getState() == 'complete'; + } + + public function prepare() + { + if (!$this->isPrepared()) { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be prepared.'); + } + + // If no response processing value was specified, then attempt to use the highest level of processing + if (!isset($this[self::RESPONSE_PROCESSING])) { + $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL; + } + + // Notify subscribers of the client that the command is being prepared + $this->client->dispatch('command.before_prepare', array('command' => $this)); + + // Fail on missing required arguments, and change parameters via filters + $this->validate(); + // Delegate to the subclass that implements the build method + $this->build(); + + // Add custom request headers set on the command + if ($headers = $this[self::HEADERS_OPTION]) { + foreach ($headers as $key => $value) { + $this->request->setHeader($key, $value); + } + } + + // Add any curl options to the request + if ($options = $this[Client::CURL_OPTIONS]) { + $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options)); + } + + // Set a custom response body + if ($responseBody = $this[self::RESPONSE_BODY]) { + $this->request->setResponseBody($responseBody); + } + + $this->client->dispatch('command.after_prepare', array('command' => $this)); + } + + return $this->request; + } + + /** + * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is + * set, then the command will validate using the default {@see SchemaValidator}. + * + * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema + * + * @return self + */ + public function setValidator(ValidatorInterface $validator) + { + $this->validator = $validator; + + return $this; + } + + public function getRequestHeaders() + { + return $this[self::HEADERS_OPTION]; + } + + /** + * Initialize the command (hook that can be implemented in subclasses) + */ + protected function init() {} + + /** + * Create the request object that will carry out the command + */ + abstract protected function build(); + + /** + * Hook used to create an operation for concrete commands that are not associated with a service description + * + * @return OperationInterface + */ + protected function createOperation() + { + return new Operation(array('name' => get_class($this))); + } + + /** + * Create the result of the command after the request has been completed. + * Override this method in subclasses to customize this behavior + */ + protected function process() + { + $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW + ? DefaultResponseParser::getInstance()->parse($this) + : $this->request->getResponse(); + } + + /** + * Validate and prepare the command based on the schema and rules defined by the command's Operation object + * + * @throws ValidationException when validation errors occur + */ + protected function validate() + { + // Do not perform request validation/transformation if it is disable + if ($this[self::DISABLE_VALIDATION]) { + return; + } + + $errors = array(); + $validator = $this->getValidator(); + foreach ($this->operation->getParams() as $name => $schema) { + $value = $this[$name]; + if (!$validator->validate($schema, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + // Update the config value if it changed and no validation errors were encountered + $this->data[$name] = $value; + } + } + + // Validate additional parameters + $hidden = $this[self::HIDDEN_PARAMS]; + + if ($properties = $this->operation->getAdditionalParameters()) { + foreach ($this->toArray() as $name => $value) { + // It's only additional if it isn't defined in the schema + if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) { + // Always set the name so that error messages are useful + $properties->setName($name); + if (!$validator->validate($properties, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + $this->data[$name] = $value; + } + } + } + } + + if (!empty($errors)) { + $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); + $e->setErrors($errors); + throw $e; + } + } + + /** + * Get the validator used to prepare and validate properties. If no validator has been set on the command, then + * the default {@see SchemaValidator} will be used. + * + * @return ValidatorInterface + */ + protected function getValidator() + { + if (!$this->validator) { + $this->validator = SchemaValidator::getInstance(); + } + + return $this->validator; + } + + /** + * Get array of any validation errors + * If no validator has been set then return false + */ + public function getValidationErrors() + { + if (!$this->validator) { + return false; + } + + return $this->validator->getErrors(); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php new file mode 100644 index 0000000..cb6ac40 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php @@ -0,0 +1,41 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Common\Exception\UnexpectedValueException; +use Guzzle\Http\Message\RequestInterface; + +/** + * A ClosureCommand is a command that allows dynamic commands to be created at runtime using a closure to prepare the + * request. A closure key and \Closure value must be passed to the command in the constructor. The closure must + * accept the command object as an argument. + */ +class ClosureCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + * @throws InvalidArgumentException if a closure was not passed + */ + protected function init() + { + if (!$this['closure']) { + throw new InvalidArgumentException('A closure must be passed in the parameters array'); + } + } + + /** + * {@inheritdoc} + * @throws UnexpectedValueException If the closure does not return a request + */ + protected function build() + { + $closure = $this['closure']; + /** @var $closure \Closure */ + $this->request = $closure($this, $this->operation); + + if (!$this->request || !$this->request instanceof RequestInterface) { + throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php new file mode 100644 index 0000000..fbb61d2 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php @@ -0,0 +1,128 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Common\Collection; +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Http\Message\Response; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Exception\CommandException; +use Guzzle\Service\Description\OperationInterface; +use Guzzle\Service\ClientInterface; +use Guzzle\Common\ToArrayInterface; + +/** + * A command object that contains parameters that can be modified and accessed like an array and turned into an array + */ +interface CommandInterface extends \ArrayAccess, ToArrayInterface +{ + /** + * Get the short form name of the command + * + * @return string + */ + public function getName(); + + /** + * Get the API operation information about the command + * + * @return OperationInterface + */ + public function getOperation(); + + /** + * Execute the command and return the result + * + * @return mixed Returns the result of {@see CommandInterface::execute} + * @throws CommandException if a client has not been associated with the command + */ + public function execute(); + + /** + * Get the client object that will execute the command + * + * @return ClientInterface|null + */ + public function getClient(); + + /** + * Set the client object that will execute the command + * + * @param ClientInterface $client The client object that will execute the command + * + * @return self + */ + public function setClient(ClientInterface $client); + + /** + * Get the request object associated with the command + * + * @return RequestInterface + * @throws CommandException if the command has not been executed + */ + public function getRequest(); + + /** + * Get the response object associated with the command + * + * @return Response + * @throws CommandException if the command has not been executed + */ + public function getResponse(); + + /** + * Get the result of the command + * + * @return Response By default, commands return a Response object unless overridden in a subclass + * @throws CommandException if the command has not been executed + */ + public function getResult(); + + /** + * Set the result of the command + * + * @param mixed $result Result to set + * + * @return self + */ + public function setResult($result); + + /** + * Returns TRUE if the command has been prepared for executing + * + * @return bool + */ + public function isPrepared(); + + /** + * Returns TRUE if the command has been executed + * + * @return bool + */ + public function isExecuted(); + + /** + * Prepare the command for executing and create a request object. + * + * @return RequestInterface Returns the generated request + * @throws CommandException if a client object has not been set previously or in the prepare() + */ + public function prepare(); + + /** + * Get the object that manages the request headers that will be set on any outbound requests from the command + * + * @return Collection + */ + public function getRequestHeaders(); + + /** + * Specify a callable to execute when the command completes + * + * @param mixed $callable Callable to execute when the command completes. The callable must accept a + * {@see CommandInterface} object as the only argument. + * @return self + * @throws InvalidArgumentException + */ + public function setOnComplete($callable); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php new file mode 100644 index 0000000..e050678 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php @@ -0,0 +1,32 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Common\Event; + +/** + * Event class emitted with the operation.parse_class event + */ +class CreateResponseClassEvent extends Event +{ + /** + * Set the result of the object creation + * + * @param mixed $result Result value to set + */ + public function setResult($result) + { + $this['result'] = $result; + $this->stopPropagation(); + } + + /** + * Get the created object + * + * @return mixed + */ + public function getResult() + { + return $this['result']; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php new file mode 100644 index 0000000..2dc4acd --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php @@ -0,0 +1,169 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface; +use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight; +use Guzzle\Service\Description\OperationInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Default request serializer that transforms command options and operation parameters into a request + */ +class DefaultRequestSerializer implements RequestSerializerInterface +{ + /** @var VisitorFlyweight $factory Visitor factory */ + protected $factory; + + /** @var self */ + protected static $instance; + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(VisitorFlyweight::getInstance()); + } + + return self::$instance; + } + + /** + * @param VisitorFlyweight $factory Factory to use when creating visitors + */ + public function __construct(VisitorFlyweight $factory) + { + $this->factory = $factory; + } + + /** + * Add a location visitor to the serializer + * + * @param string $location Location to associate with the visitor + * @param RequestVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, RequestVisitorInterface $visitor) + { + $this->factory->addRequestVisitor($location, $visitor); + + return $this; + } + + public function prepare(CommandInterface $command) + { + $request = $this->createRequest($command); + // Keep an array of visitors found in the operation + $foundVisitors = array(); + $operation = $command->getOperation(); + + // Add arguments to the request using the location attribute + foreach ($operation->getParams() as $name => $arg) { + /** @var $arg \Guzzle\Service\Description\Parameter */ + $location = $arg->getLocation(); + // Skip 'uri' locations because they've already been processed + if ($location && $location != 'uri') { + // Instantiate visitors as they are detected in the properties + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getRequestVisitor($location); + } + // Ensure that a value has been set for this parameter + $value = $command[$name]; + if ($value !== null) { + // Apply the parameter value with the location visitor + $foundVisitors[$location]->visit($command, $request, $arg, $value); + } + } + } + + // Serialize additional parameters + if ($additional = $operation->getAdditionalParameters()) { + if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) { + $foundVisitors[$additional->getLocation()] = $visitor; + } + } + + // Call the after method on each visitor found in the operation + foreach ($foundVisitors as $visitor) { + $visitor->after($command, $request); + } + + return $request; + } + + /** + * Serialize additional parameters + * + * @param OperationInterface $operation Operation that owns the command + * @param CommandInterface $command Command to prepare + * @param RequestInterface $request Request to serialize + * @param Parameter $additional Additional parameters + * + * @return null|RequestVisitorInterface + */ + protected function prepareAdditionalParameters( + OperationInterface $operation, + CommandInterface $command, + RequestInterface $request, + Parameter $additional + ) { + if (!($location = $additional->getLocation())) { + return; + } + + $visitor = $this->factory->getRequestVisitor($location); + $hidden = $command[$command::HIDDEN_PARAMS]; + + foreach ($command->toArray() as $key => $value) { + // Ignore values that are null or built-in command options + if ($value !== null + && !in_array($key, $hidden) + && !$operation->hasParam($key) + ) { + $additional->setName($key); + $visitor->visit($command, $request, $additional, $value); + } + } + + return $visitor; + } + + /** + * Create a request for the command and operation + * + * @param CommandInterface $command Command to create a request for + * + * @return RequestInterface + */ + protected function createRequest(CommandInterface $command) + { + $operation = $command->getOperation(); + $client = $command->getClient(); + $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array(); + + // If the command does not specify a template, then assume the base URL of the client + if (!($uri = $operation->getUri())) { + return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options); + } + + // Get the path values and use the client config settings + $variables = array(); + foreach ($operation->getParams() as $name => $arg) { + if ($arg->getLocation() == 'uri') { + if (isset($command[$name])) { + $variables[$name] = $arg->filter($command[$name]); + if (!is_array($variables[$name])) { + $variables[$name] = (string) $variables[$name]; + } + } + } + } + + return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php new file mode 100644 index 0000000..4fe3803 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php @@ -0,0 +1,55 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Http\Message\Response; + +/** + * Default HTTP response parser used to marshal JSON responses into arrays and XML responses into SimpleXMLElement + */ +class DefaultResponseParser implements ResponseParserInterface +{ + /** @var self */ + protected static $instance; + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self; + } + + return self::$instance; + } + + public function parse(CommandInterface $command) + { + $response = $command->getRequest()->getResponse(); + + // Account for hard coded content-type values specified in service descriptions + if ($contentType = $command['command.expects']) { + $response->setHeader('Content-Type', $contentType); + } else { + $contentType = (string) $response->getHeader('Content-Type'); + } + + return $this->handleParsing($command, $response, $contentType); + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $result = $response; + if ($result->getBody()) { + if (stripos($contentType, 'json') !== false) { + $result = $result->json(); + } elseif (stripos($contentType, 'xml') !== false) { + $result = $result->xml(); + } + } + + return $result; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php new file mode 100644 index 0000000..1c5ce07 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php @@ -0,0 +1,39 @@ +<?php + +namespace Guzzle\Service\Command\Factory; + +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Service\ClientInterface; + +/** + * Command factory used when you need to provide aliases to commands + */ +class AliasFactory implements FactoryInterface +{ + /** @var array Associative array mapping command aliases to the aliased command */ + protected $aliases; + + /** @var ClientInterface Client used to retry using aliases */ + protected $client; + + /** + * @param ClientInterface $client Client used to retry with the alias + * @param array $aliases Associative array mapping aliases to the alias + */ + public function __construct(ClientInterface $client, array $aliases) + { + $this->client = $client; + $this->aliases = $aliases; + } + + public function factory($name, array $args = array()) + { + if (isset($this->aliases[$name])) { + try { + return $this->client->getCommand($this->aliases[$name], $args); + } catch (InvalidArgumentException $e) { + return null; + } + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php new file mode 100644 index 0000000..8c46983 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php @@ -0,0 +1,154 @@ +<?php + +namespace Guzzle\Service\Command\Factory; + +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\ClientInterface; + +/** + * Composite factory used by a client object to create command objects utilizing multiple factories + */ +class CompositeFactory implements \IteratorAggregate, \Countable, FactoryInterface +{ + /** @var array Array of command factories */ + protected $factories; + + /** + * Get the default chain to use with clients + * + * @param ClientInterface $client Client to base the chain on + * + * @return self + */ + public static function getDefaultChain(ClientInterface $client) + { + $factories = array(); + if ($description = $client->getDescription()) { + $factories[] = new ServiceDescriptionFactory($description); + } + $factories[] = new ConcreteClassFactory($client); + + return new self($factories); + } + + /** + * @param array $factories Array of command factories + */ + public function __construct(array $factories = array()) + { + $this->factories = $factories; + } + + /** + * Add a command factory to the chain + * + * @param FactoryInterface $factory Factory to add + * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object + * matching a class name. + * @return CompositeFactory + */ + public function add(FactoryInterface $factory, $before = null) + { + $pos = null; + + if ($before) { + foreach ($this->factories as $i => $f) { + if ($before instanceof FactoryInterface) { + if ($f === $before) { + $pos = $i; + break; + } + } elseif (is_string($before)) { + if ($f instanceof $before) { + $pos = $i; + break; + } + } + } + } + + if ($pos === null) { + $this->factories[] = $factory; + } else { + array_splice($this->factories, $i, 0, array($factory)); + } + + return $this; + } + + /** + * Check if the chain contains a specific command factory + * + * @param FactoryInterface|string $factory Factory to check + * + * @return bool + */ + public function has($factory) + { + return (bool) $this->find($factory); + } + + /** + * Remove a specific command factory from the chain + * + * @param string|FactoryInterface $factory Factory to remove by name or instance + * + * @return CompositeFactory + */ + public function remove($factory = null) + { + if (!($factory instanceof FactoryInterface)) { + $factory = $this->find($factory); + } + + $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { + return $f !== $factory; + })); + + return $this; + } + + /** + * Get a command factory by class name + * + * @param string|FactoryInterface $factory Command factory class or instance + * + * @return null|FactoryInterface + */ + public function find($factory) + { + foreach ($this->factories as $f) { + if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { + return $f; + } + } + } + + /** + * Create a command using the associated command factories + * + * @param string $name Name of the command + * @param array $args Command arguments + * + * @return CommandInterface + */ + public function factory($name, array $args = array()) + { + foreach ($this->factories as $factory) { + $command = $factory->factory($name, $args); + if ($command) { + return $command; + } + } + } + + public function count() + { + return count($this->factories); + } + + public function getIterator() + { + return new \ArrayIterator($this->factories); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php new file mode 100644 index 0000000..0e93dea --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php @@ -0,0 +1,47 @@ +<?php + +namespace Guzzle\Service\Command\Factory; + +use Guzzle\Inflection\InflectorInterface; +use Guzzle\Inflection\Inflector; +use Guzzle\Service\ClientInterface; + +/** + * Command factory used to create commands referencing concrete command classes + */ +class ConcreteClassFactory implements FactoryInterface +{ + /** @var ClientInterface */ + protected $client; + + /** @var InflectorInterface */ + protected $inflector; + + /** + * @param ClientInterface $client Client that owns the commands + * @param InflectorInterface $inflector Inflector used to resolve class names + */ + public function __construct(ClientInterface $client, InflectorInterface $inflector = null) + { + $this->client = $client; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + public function factory($name, array $args = array()) + { + // Determine the class to instantiate based on the namespace of the current client and the default directory + $prefix = $this->client->getConfig('command.prefix'); + if (!$prefix) { + // The prefix can be specified in a factory method and is cached + $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; + $this->client->getConfig()->set('command.prefix', $prefix); + } + + $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); + + // Create the concrete command if it exists + if (class_exists($class)) { + return new $class($args); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php new file mode 100644 index 0000000..35c299d --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php @@ -0,0 +1,21 @@ +<?php + +namespace Guzzle\Service\Command\Factory; + +use Guzzle\Service\Command\CommandInterface; + +/** + * Interface for creating commands by name + */ +interface FactoryInterface +{ + /** + * Create a command by name + * + * @param string $name Command to create + * @param array $args Command arguments + * + * @return CommandInterface|null + */ + public function factory($name, array $args = array()); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php new file mode 100644 index 0000000..0ad80bc --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php @@ -0,0 +1,27 @@ +<?php + +namespace Guzzle\Service\Command\Factory; + +/** + * Command factory used when explicitly mapping strings to command classes + */ +class MapFactory implements FactoryInterface +{ + /** @var array Associative array mapping command names to classes */ + protected $map; + + /** @param array $map Associative array mapping command names to classes */ + public function __construct(array $map) + { + $this->map = $map; + } + + public function factory($name, array $args = array()) + { + if (isset($this->map[$name])) { + $class = $this->map[$name]; + + return new $class($args); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php new file mode 100644 index 0000000..b943a5b --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php @@ -0,0 +1,71 @@ +<?php + +namespace Guzzle\Service\Command\Factory; + +use Guzzle\Service\Description\ServiceDescriptionInterface; +use Guzzle\Inflection\InflectorInterface; + +/** + * Command factory used to create commands based on service descriptions + */ +class ServiceDescriptionFactory implements FactoryInterface +{ + /** @var ServiceDescriptionInterface */ + protected $description; + + /** @var InflectorInterface */ + protected $inflector; + + /** + * @param ServiceDescriptionInterface $description Service description + * @param InflectorInterface $inflector Optional inflector to use if the command is not at first found + */ + public function __construct(ServiceDescriptionInterface $description, InflectorInterface $inflector = null) + { + $this->setServiceDescription($description); + $this->inflector = $inflector; + } + + /** + * Change the service description used with the factory + * + * @param ServiceDescriptionInterface $description Service description to use + * + * @return FactoryInterface + */ + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the service description + * + * @return ServiceDescriptionInterface + */ + public function getServiceDescription() + { + return $this->description; + } + + public function factory($name, array $args = array()) + { + $command = $this->description->getOperation($name); + + // If a command wasn't found, then try to uppercase the first letter and try again + if (!$command) { + $command = $this->description->getOperation(ucfirst($name)); + // If an inflector was passed, then attempt to get the command using snake_case inflection + if (!$command && $this->inflector) { + $command = $this->description->getOperation($this->inflector->snake($name)); + } + } + + if ($command) { + $class = $command->getClass(); + return new $class($args, $command, $this->description); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php new file mode 100644 index 0000000..adcfca1 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php @@ -0,0 +1,69 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Description\Parameter; + +abstract class AbstractRequestVisitor implements RequestVisitorInterface +{ + /** + * @codeCoverageIgnore + */ + public function after(CommandInterface $command, RequestInterface $request) {} + + /** + * @codeCoverageIgnore + */ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) {} + + /** + * Prepare (filter and set desired name for request item) the value for request. + * + * @param mixed $value + * @param \Guzzle\Service\Description\Parameter $param + * + * @return array|mixed + */ + protected function prepareValue($value, Parameter $param) + { + return is_array($value) + ? $this->resolveRecursively($value, $param) + : $param->filter($value); + } + + /** + * Map nested parameters into the location_key based parameters + * + * @param array $value Value to map + * @param Parameter $param Parameter that holds information about the current key + * + * @return array Returns the mapped array + */ + protected function resolveRecursively(array $value, Parameter $param) + { + foreach ($value as $name => &$v) { + switch ($param->getType()) { + case 'object': + if ($subParam = $param->getProperty($name)) { + $key = $subParam->getWireName(); + $value[$key] = $this->prepareValue($v, $subParam); + if ($name != $key) { + unset($value[$name]); + } + } elseif ($param->getAdditionalProperties() instanceof Parameter) { + $v = $this->prepareValue($v, $param->getAdditionalProperties()); + } + break; + case 'array': + if ($items = $param->getItems()) { + $v = $this->prepareValue($v, $items); + } + break; + } + } + + return $param->filter($value); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php new file mode 100644 index 0000000..168d780 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php @@ -0,0 +1,58 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\EntityBody; +use Guzzle\Http\Message\EntityEnclosingRequestInterface; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\EntityBodyInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to apply a body to a request + * + * This visitor can use a data parameter of 'expect' to control the Expect header. Set the expect data parameter to + * false to disable the expect header, or set the value to an integer so that the expect 100-continue header is only + * added if the Content-Length of the entity body is greater than the value. + */ +class BodyVisitor extends AbstractRequestVisitor +{ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $value = $param->filter($value); + $entityBody = EntityBody::factory($value); + $request->setBody($entityBody); + $this->addExpectHeader($request, $entityBody, $param->getData('expect_header')); + // Add the Content-Encoding header if one is set on the EntityBody + if ($encoding = $entityBody->getContentEncoding()) { + $request->setHeader('Content-Encoding', $encoding); + } + } + + /** + * Add the appropriate expect header to a request + * + * @param EntityEnclosingRequestInterface $request Request to update + * @param EntityBodyInterface $body Entity body of the request + * @param string|int $expect Expect header setting + */ + protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect) + { + // Allow the `expect` data parameter to be set to remove the Expect header from the request + if ($expect === false) { + $request->removeHeader('Expect'); + } elseif ($expect !== true) { + // Default to using a MB as the point in which to start using the expect header + $expect = $expect ?: 1048576; + // If the expect_header value is numeric then only add if the size is greater than the cutoff + if (is_numeric($expect) && $body->getSize()) { + if ($body->getSize() < $expect) { + $request->removeHeader('Expect'); + } else { + $request->setHeader('Expect', '100-Continue'); + } + } + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php new file mode 100644 index 0000000..2a53754 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php @@ -0,0 +1,44 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to apply a parameter to a header value + */ +class HeaderVisitor extends AbstractRequestVisitor +{ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $value = $param->filter($value); + if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->addPrefixedHeaders($request, $param, $value); + } else { + $request->setHeader($param->getWireName(), $value); + } + } + + /** + * Add a prefixed array of headers to the request + * + * @param RequestInterface $request Request to update + * @param Parameter $param Parameter object + * @param array $value Header array to add + * + * @throws InvalidArgumentException + */ + protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value) + { + if (!is_array($value)) { + throw new InvalidArgumentException('An array of mapped headers expected, but received a single value'); + } + $prefix = $param->getSentAs(); + foreach ($value as $headerName => $headerValue) { + $request->setHeader($prefix . $headerName, $headerValue); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php new file mode 100644 index 0000000..757e1c5 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php @@ -0,0 +1,63 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to apply a parameter to an array that will be serialized as a top level key-value pair in a JSON body + */ +class JsonVisitor extends AbstractRequestVisitor +{ + /** @var bool Whether or not to add a Content-Type header when JSON is found */ + protected $jsonContentType = 'application/json'; + + /** @var \SplObjectStorage Data object for persisting JSON data */ + protected $data; + + public function __construct() + { + $this->data = new \SplObjectStorage(); + } + + /** + * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a + * Content-Type header unless you specify one here. + * + * @param string $header Header to set when JSON is added (e.g. application/json) + * + * @return self + */ + public function setContentTypeHeader($header = 'application/json') + { + $this->jsonContentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + if (isset($this->data[$command])) { + $json = $this->data[$command]; + } else { + $json = array(); + } + $json[$param->getWireName()] = $this->prepareValue($value, $param); + $this->data[$command] = $json; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + if (isset($this->data[$command])) { + // Don't overwrite the Content-Type if one is set + if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->jsonContentType); + } + + $request->setBody(json_encode($this->data[$command])); + unset($this->data[$command]); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php new file mode 100644 index 0000000..975850b --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php @@ -0,0 +1,18 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to apply a parameter to a POST field + */ +class PostFieldVisitor extends AbstractRequestVisitor +{ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $request->setPostField($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php new file mode 100644 index 0000000..0853ebe --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php @@ -0,0 +1,24 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\PostFileInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to apply a parameter to a POST file + */ +class PostFileVisitor extends AbstractRequestVisitor +{ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $value = $param->filter($value); + if ($value instanceof PostFileInterface) { + $request->addPostFile($value); + } else { + $request->addPostFile($param->getWireName(), $value); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php new file mode 100644 index 0000000..315877a --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php @@ -0,0 +1,18 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to apply a parameter to a request's query string + */ +class QueryVisitor extends AbstractRequestVisitor +{ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $request->getQuery()->set($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php new file mode 100644 index 0000000..14e0b2d --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php @@ -0,0 +1,31 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to add values to different locations in a request with different behaviors as needed + */ +interface RequestVisitorInterface +{ + /** + * Called after visiting all parameters + * + * @param CommandInterface $command Command being visited + * @param RequestInterface $request Request being visited + */ + public function after(CommandInterface $command, RequestInterface $request); + + /** + * Called once for each parameter being visited that matches the location type + * + * @param CommandInterface $command Command being visited + * @param RequestInterface $request Request being visited + * @param Parameter $param Parameter being visited + * @param mixed $value Value to set + */ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php new file mode 100644 index 0000000..09f35f8 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php @@ -0,0 +1,18 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to change the location in which a response body is saved + */ +class ResponseBodyVisitor extends AbstractRequestVisitor +{ + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $request->setResponseBody($value); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php new file mode 100644 index 0000000..5b71487 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php @@ -0,0 +1,252 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Request; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Service\Description\Operation; +use Guzzle\Service\Description\Parameter; + +/** + * Location visitor used to serialize XML bodies + */ +class XmlVisitor extends AbstractRequestVisitor +{ + /** @var \SplObjectStorage Data object for persisting XML data */ + protected $data; + + /** @var bool Content-Type header added when XML is found */ + protected $contentType = 'application/xml'; + + public function __construct() + { + $this->data = new \SplObjectStorage(); + } + + /** + * Change the content-type header that is added when XML is found + * + * @param string $header Header to set when XML is found + * + * @return self + */ + public function setContentTypeHeader($header) + { + $this->contentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $xml = isset($this->data[$command]) + ? $this->data[$command] + : $this->createRootElement($param->getParent()); + $this->addXml($xml, $param, $value); + + $this->data[$command] = $xml; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + $xml = null; + + // If data was found that needs to be serialized, then do so + if (isset($this->data[$command])) { + $xml = $this->finishDocument($this->data[$command]); + unset($this->data[$command]); + } else { + // Check if XML should always be sent for the command + $operation = $command->getOperation(); + if ($operation->getData('xmlAllowEmpty')) { + $xmlWriter = $this->createRootElement($operation); + $xml = $this->finishDocument($xmlWriter); + } + } + + if ($xml) { + // Don't overwrite the Content-Type if one is set + if ($this->contentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->contentType); + } + $request->setBody($xml); + } + } + + /** + * Create the root XML element to use with a request + * + * @param Operation $operation Operation object + * + * @return \XMLWriter + */ + protected function createRootElement(Operation $operation) + { + static $defaultRoot = array('name' => 'Request'); + // If no root element was specified, then just wrap the XML in 'Request' + $root = $operation->getData('xmlRoot') ?: $defaultRoot; + // Allow the XML declaration to be customized with xmlEncoding + $encoding = $operation->getData('xmlEncoding'); + + $xmlWriter = $this->startDocument($encoding); + + $xmlWriter->startElement($root['name']); + // Create the wrapping element with no namespaces if no namespaces were present + if (!empty($root['namespaces'])) { + // Create the wrapping element with an array of one or more namespaces + foreach ((array) $root['namespaces'] as $prefix => $uri) { + $nsLabel = 'xmlns'; + if (!is_numeric($prefix)) { + $nsLabel .= ':'.$prefix; + } + $xmlWriter->writeAttribute($nsLabel, $uri); + } + } + return $xmlWriter; + } + + /** + * Recursively build the XML body + * + * @param \XMLWriter $xmlWriter XML to modify + * @param Parameter $param API Parameter + * @param mixed $value Value to add + */ + protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value) + { + if ($value === null) { + return; + } + + $value = $param->filter($value); + $type = $param->getType(); + $name = $param->getWireName(); + $prefix = null; + $namespace = $param->getData('xmlNamespace'); + if (false !== strpos($name, ':')) { + list($prefix, $name) = explode(':', $name, 2); + } + + if ($type == 'object' || $type == 'array') { + if (!$param->getData('xmlFlattened')) { + $xmlWriter->startElementNS(null, $name, $namespace); + } + if ($param->getType() == 'array') { + $this->addXmlArray($xmlWriter, $param, $value); + } elseif ($param->getType() == 'object') { + $this->addXmlObject($xmlWriter, $param, $value); + } + if (!$param->getData('xmlFlattened')) { + $xmlWriter->endElement(); + } + return; + } + if ($param->getData('xmlAttribute')) { + $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value); + } else { + $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value); + } + } + + /** + * Write an attribute with namespace if used + * + * @param \XMLWriter $xmlWriter XMLWriter instance + * @param string $prefix Namespace prefix if any + * @param string $name Attribute name + * @param string $namespace The uri of the namespace + * @param string $value The attribute content + */ + protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value) + { + if (empty($namespace)) { + $xmlWriter->writeAttribute($name, $value); + } else { + $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value); + } + } + + /** + * Write an element with namespace if used + * + * @param \XMLWriter $xmlWriter XML writer resource + * @param string $prefix Namespace prefix if any + * @param string $name Element name + * @param string $namespace The uri of the namespace + * @param string $value The element content + */ + protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value) + { + $xmlWriter->startElementNS($prefix, $name, $namespace); + if (strpbrk($value, '<>&')) { + $xmlWriter->writeCData($value); + } else { + $xmlWriter->writeRaw($value); + } + $xmlWriter->endElement(); + } + + /** + * Create a new xml writer and start a document + * + * @param string $encoding document encoding + * + * @return \XMLWriter the writer resource + */ + protected function startDocument($encoding) + { + $xmlWriter = new \XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->startDocument('1.0', $encoding); + + return $xmlWriter; + } + + /** + * End the document and return the output + * + * @param \XMLWriter $xmlWriter + * + * @return \string the writer resource + */ + protected function finishDocument($xmlWriter) + { + $xmlWriter->endDocument(); + + return $xmlWriter->outputMemory(); + } + + /** + * Add an array to the XML + */ + protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + if ($items = $param->getItems()) { + foreach ($value as $v) { + $this->addXml($xmlWriter, $items, $v); + } + } + } + + /** + * Add an object to the XML + */ + protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + $noAttributes = array(); + // add values which have attributes + foreach ($value as $name => $v) { + if ($property = $param->getProperty($name)) { + if ($property->getData('xmlAttribute')) { + $this->addXml($xmlWriter, $property, $v); + } else { + $noAttributes[] = array('value' => $v, 'property' => $property); + } + } + } + // now add values with no attributes + foreach ($noAttributes as $element) { + $this->addXml($xmlWriter, $element['property'], $element['value']); + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php new file mode 100644 index 0000000..d87eeb9 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php @@ -0,0 +1,26 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; + +/** + * {@inheritdoc} + * @codeCoverageIgnore + */ +abstract class AbstractResponseVisitor implements ResponseVisitorInterface +{ + public function before(CommandInterface $command, array &$result) {} + + public function after(CommandInterface $command) {} + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) {} +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php new file mode 100644 index 0000000..f70b727 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php @@ -0,0 +1,23 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Service\Command\CommandInterface; +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; + +/** + * Visitor used to add the body of a response to a particular key + */ +class BodyVisitor extends AbstractResponseVisitor +{ + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $value[$param->getName()] = $param->filter($response->getBody()); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php new file mode 100644 index 0000000..0f8737c --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php @@ -0,0 +1,50 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to add a particular header of a response to a key in the result + */ +class HeaderVisitor extends AbstractResponseVisitor +{ + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->processPrefixedHeaders($response, $param, $value); + } else { + $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName())); + } + } + + /** + * Process a prefixed header array + * + * @param Response $response Response that contains the headers + * @param Parameter $param Parameter object + * @param array $value Value response array to modify + */ + protected function processPrefixedHeaders(Response $response, Parameter $param, &$value) + { + // Grab prefixed headers that should be placed into an array with the prefix stripped + if ($prefix = $param->getSentAs()) { + $container = $param->getName(); + $len = strlen($prefix); + // Find all matching headers and place them into the containing element + foreach ($response->getHeaders()->toArray() as $key => $header) { + if (stripos($key, $prefix) === 0) { + // Account for multi-value headers + $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header; + } + } + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php new file mode 100644 index 0000000..a609ebd --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php @@ -0,0 +1,93 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to marshal JSON response data into a formatted array. + * + * Allows top level JSON parameters to be inserted into the result of a command. The top level attributes are grabbed + * from the response's JSON data using the name value by default. Filters can be applied to parameters as they are + * traversed. This allows data to be normalized before returning it to users (for example converting timestamps to + * DateTime objects). + */ +class JsonVisitor extends AbstractResponseVisitor +{ + public function before(CommandInterface $command, array &$result) + { + // Ensure that the result of the command is always rooted with the parsed JSON data + $result = $command->getResponse()->json(); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $name = $param->getName(); + $key = $param->getWireName(); + if (isset($value[$key])) { + $this->recursiveProcess($param, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + if ($value === null) { + return; + } + + if (is_array($value)) { + $type = $param->getType(); + if ($type == 'array') { + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } elseif ($type == 'object' && !isset($value[0])) { + // On the above line, we ensure that the array is associative and not numerically indexed + $knownProperties = array(); + if ($properties = $param->getProperties()) { + foreach ($properties as $property) { + $name = $property->getName(); + $key = $property->getWireName(); + $knownProperties[$name] = 1; + if (isset($value[$key])) { + $this->recursiveProcess($property, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } elseif (($additional = $param->getAdditionalProperties()) !== true) { + // Validate and filter additional properties + foreach ($value as &$v) { + $this->recursiveProcess($additional, $v); + } + } + } + } + + $value = $param->filter($value); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php new file mode 100644 index 0000000..1b10ebc --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php @@ -0,0 +1,23 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to add the reason phrase of a response to a key in the result + */ +class ReasonPhraseVisitor extends AbstractResponseVisitor +{ + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $value[$param->getName()] = $response->getReasonPhrase(); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php new file mode 100644 index 0000000..033f40c --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php @@ -0,0 +1,46 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to parse values out of a response into an associative array + */ +interface ResponseVisitorInterface +{ + /** + * Called before visiting all parameters. This can be used for seeding the result of a command with default + * data (e.g. populating with JSON data in the response then adding to the parsed data). + * + * @param CommandInterface $command Command being visited + * @param array $result Result value to update if needed (e.g. parsing XML or JSON) + */ + public function before(CommandInterface $command, array &$result); + + /** + * Called after visiting all parameters + * + * @param CommandInterface $command Command being visited + */ + public function after(CommandInterface $command); + + /** + * Called once for each parameter being visited that matches the location type + * + * @param CommandInterface $command Command being visited + * @param Response $response Response being visited + * @param Parameter $param Parameter being visited + * @param mixed $value Result associative array value being updated by reference + * @param mixed $context Parsing context + */ + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php new file mode 100644 index 0000000..00c5ce0 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php @@ -0,0 +1,23 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to add the status code of a response to a key in the result + */ +class StatusCodeVisitor extends AbstractResponseVisitor +{ + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $value[$param->getName()] = $response->getStatusCode(); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php new file mode 100644 index 0000000..bb7124b --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php @@ -0,0 +1,151 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor\Response; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Command\CommandInterface; + +/** + * Location visitor used to marshal XML response data into a formatted array + */ +class XmlVisitor extends AbstractResponseVisitor +{ + public function before(CommandInterface $command, array &$result) + { + // Set the result of the command to the array conversion of the XML body + $result = json_decode(json_encode($command->getResponse()->xml()), true); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $sentAs = $param->getWireName(); + $name = $param->getName(); + if (isset($value[$sentAs])) { + $this->recursiveProcess($param, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being processed + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + $type = $param->getType(); + + if (!is_array($value)) { + if ($type == 'array') { + // Cast to an array if the value was a string, but should be an array + $this->recursiveProcess($param->getItems(), $value); + $value = array($value); + } + } elseif ($type == 'object') { + $this->processObject($param, $value); + } elseif ($type == 'array') { + $this->processArray($param, $value); + } elseif ($type == 'string' && gettype($value) == 'array') { + $value = ''; + } + + if ($value !== null) { + $value = $param->filter($value); + } + } + + /** + * Process an array + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processArray(Parameter $param, &$value) + { + // Convert the node if it was meant to be an array + if (!isset($value[0])) { + // Collections fo nodes are sometimes wrapped in an additional array. For example: + // <Items><Item><a>1</a></Item><Item><a>2</a></Item></Items> should become: + // array('Items' => array(array('a' => 1), array('a' => 2)) + // Some nodes are not wrapped. For example: <Foo><a>1</a></Foo><Foo><a>2</a></Foo> + // should become array('Foo' => array(array('a' => 1), array('a' => 2)) + if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) { + // Account for the case of a collection wrapping wrapped nodes: Items => Item[] + $value = $value[$param->getItems()->getWireName()]; + // If the wrapped node only had one value, then make it an array of nodes + if (!isset($value[0]) || !is_array($value)) { + $value = array($value); + } + } elseif (!empty($value)) { + // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the + // value is set and not empty + $value = array($value); + } + } + + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } + + /** + * Process an object + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processObject(Parameter $param, &$value) + { + // Ensure that the array is associative and not numerically indexed + if (!isset($value[0]) && ($properties = $param->getProperties())) { + $knownProperties = array(); + foreach ($properties as $property) { + $name = $property->getName(); + $sentAs = $property->getWireName(); + $knownProperties[$name] = 1; + if ($property->getData('xmlAttribute')) { + $this->processXmlAttribute($property, $value); + } elseif (isset($value[$sentAs])) { + $this->recursiveProcess($property, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } + } + } + + /** + * Process an XML attribute property + * + * @param Parameter $property Property to process + * @param array $value Value to process and update + */ + protected function processXmlAttribute(Parameter $property, array &$value) + { + $sentAs = $property->getWireName(); + if (isset($value['@attributes'][$sentAs])) { + $value[$property->getName()] = $value['@attributes'][$sentAs]; + unset($value['@attributes'][$sentAs]); + if (empty($value['@attributes'])) { + unset($value['@attributes']); + } + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php new file mode 100644 index 0000000..74cb628 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php @@ -0,0 +1,138 @@ +<?php + +namespace Guzzle\Service\Command\LocationVisitor; + +use Guzzle\Common\Exception\InvalidArgumentException; +use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface; +use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface; + +/** + * Flyweight factory used to instantiate request and response visitors + */ +class VisitorFlyweight +{ + /** @var self Singleton instance of self */ + protected static $instance; + + /** @var array Default array of mappings of location names to classes */ + protected static $defaultMappings = array( + 'request.body' => 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor', + 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor', + 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', + 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor', + 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor', + 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor', + 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor', + 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor', + 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor', + 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', + 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor', + 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor', + 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor' + ); + + /** @var array Array of mappings of location names to classes */ + protected $mappings; + + /** @var array Cache of instantiated visitors */ + protected $cache = array(); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to + * use the default values. + */ + public function __construct(array $mappings = null) + { + $this->mappings = $mappings === null ? self::$defaultMappings : $mappings; + } + + /** + * Get an instance of a request visitor by location name + * + * @param string $visitor Visitor name + * + * @return RequestVisitorInterface + */ + public function getRequestVisitor($visitor) + { + return $this->getKey('request.' . $visitor); + } + + /** + * Get an instance of a response visitor by location name + * + * @param string $visitor Visitor name + * + * @return ResponseVisitorInterface + */ + public function getResponseVisitor($visitor) + { + return $this->getKey('response.' . $visitor); + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param RequestVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addRequestVisitor($name, RequestVisitorInterface $visitor) + { + $this->cache['request.' . $name] = $visitor; + + return $this; + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param ResponseVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addResponseVisitor($name, ResponseVisitorInterface $visitor) + { + $this->cache['response.' . $name] = $visitor; + + return $this; + } + + /** + * Get a visitor by key value name + * + * @param string $key Key name to retrieve + * + * @return mixed + * @throws InvalidArgumentException + */ + private function getKey($key) + { + if (!isset($this->cache[$key])) { + if (!isset($this->mappings[$key])) { + list($type, $name) = explode('.', $key); + throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}"); + } + $this->cache[$key] = new $this->mappings[$key]; + } + + return $this->cache[$key]; + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php new file mode 100644 index 0000000..0748b5a --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php @@ -0,0 +1,89 @@ +<?php + +namespace Guzzle\Service\Command; + +/** + * A command that creates requests based on {@see Guzzle\Service\Description\OperationInterface} objects, and if the + * matching operation uses a service description model in the responseClass attribute, then this command will marshal + * the response into an associative array based on the JSON schema of the model. + */ +class OperationCommand extends AbstractCommand +{ + /** @var RequestSerializerInterface */ + protected $requestSerializer; + + /** @var ResponseParserInterface Response parser */ + protected $responseParser; + + /** + * Set the response parser used with the command + * + * @param ResponseParserInterface $parser Response parser + * + * @return self + */ + public function setResponseParser(ResponseParserInterface $parser) + { + $this->responseParser = $parser; + + return $this; + } + + /** + * Set the request serializer used with the command + * + * @param RequestSerializerInterface $serializer Request serializer + * + * @return self + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->requestSerializer = $serializer; + + return $this; + } + + /** + * Get the request serializer used with the command + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + if (!$this->requestSerializer) { + // Use the default request serializer if none was found + $this->requestSerializer = DefaultRequestSerializer::getInstance(); + } + + return $this->requestSerializer; + } + + /** + * Get the response parser used for the operation + * + * @return ResponseParserInterface + */ + public function getResponseParser() + { + if (!$this->responseParser) { + // Use the default response parser if none was found + $this->responseParser = OperationResponseParser::getInstance(); + } + + return $this->responseParser; + } + + protected function build() + { + // Prepare and serialize the request + $this->request = $this->getRequestSerializer()->prepare($this); + } + + protected function process() + { + // Do not process the response if 'command.response_processing' is set to 'raw' + $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW + ? $this->request->getResponse() + : $this->getResponseParser()->parse($this); + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php new file mode 100644 index 0000000..ca00bc0 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php @@ -0,0 +1,195 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Http\Message\Response; +use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight; +use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface; +use Guzzle\Service\Description\Parameter; +use Guzzle\Service\Description\OperationInterface; +use Guzzle\Service\Description\Operation; +use Guzzle\Service\Exception\ResponseClassException; +use Guzzle\Service\Resource\Model; + +/** + * Response parser that attempts to marshal responses into an associative array based on models in a service description + */ +class OperationResponseParser extends DefaultResponseParser +{ + /** @var VisitorFlyweight $factory Visitor factory */ + protected $factory; + + /** @var self */ + protected static $instance; + + /** @var bool */ + private $schemaInModels; + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!static::$instance) { + static::$instance = new static(VisitorFlyweight::getInstance()); + } + + return static::$instance; + } + + /** + * @param VisitorFlyweight $factory Factory to use when creating visitors + * @param bool $schemaInModels Set to true to inject schemas into models + */ + public function __construct(VisitorFlyweight $factory, $schemaInModels = false) + { + $this->factory = $factory; + $this->schemaInModels = $schemaInModels; + } + + /** + * Add a location visitor to the command + * + * @param string $location Location to associate with the visitor + * @param ResponseVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, ResponseVisitorInterface $visitor) + { + $this->factory->addResponseVisitor($location, $visitor); + + return $this; + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $operation = $command->getOperation(); + $type = $operation->getResponseType(); + $model = null; + + if ($type == OperationInterface::TYPE_MODEL) { + $model = $operation->getServiceDescription()->getModel($operation->getResponseClass()); + } elseif ($type == OperationInterface::TYPE_CLASS) { + return $this->parseClass($command); + } + + if (!$model) { + // Return basic processing if the responseType is not model or the model cannot be found + return parent::handleParsing($command, $response, $contentType); + } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) { + // Returns a model with no visiting if the command response processing is not model + return new Model(parent::handleParsing($command, $response, $contentType)); + } else { + // Only inject the schema into the model if "schemaInModel" is true + return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null); + } + } + + /** + * Parse a class object + * + * @param CommandInterface $command Command to parse into an object + * + * @return mixed + * @throws ResponseClassException + */ + protected function parseClass(CommandInterface $command) + { + // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result + $event = new CreateResponseClassEvent(array('command' => $command)); + $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event); + if ($result = $event->getResult()) { + return $result; + } + + $className = $command->getOperation()->getResponseClass(); + if (!method_exists($className, 'fromCommand')) { + throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method"); + } + + return $className::fromCommand($command); + } + + /** + * Perform transformations on the result array + * + * @param Parameter $model Model that defines the structure + * @param CommandInterface $command Command that performed the operation + * @param Response $response Response received + * + * @return array Returns the array of result data + */ + protected function visitResult(Parameter $model, CommandInterface $command, Response $response) + { + $foundVisitors = $result = $knownProps = array(); + $props = $model->getProperties(); + + foreach ($props as $schema) { + if ($location = $schema->getLocation()) { + // Trigger the before method on the first found visitor of this type + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + } + } + + // Visit additional properties when it is an actual schema + if (($additional = $model->getAdditionalProperties()) instanceof Parameter) { + $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors); + } + + // Apply the parameter value with the location visitor + foreach ($props as $schema) { + $knownProps[$schema->getName()] = 1; + if ($location = $schema->getLocation()) { + $foundVisitors[$location]->visit($command, $response, $schema, $result); + } + } + + // Remove any unknown and potentially unsafe top-level properties + if ($additional === false) { + $result = array_intersect_key($result, $knownProps); + } + + // Call the after() method of each found visitor + foreach ($foundVisitors as $visitor) { + $visitor->after($command); + } + + return $result; + } + + protected function visitAdditionalProperties( + Parameter $model, + CommandInterface $command, + Response $response, + Parameter $additional, + &$result, + array &$foundVisitors + ) { + // Only visit when a location is specified + if ($location = $additional->getLocation()) { + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + // Only traverse if an array was parsed from the before() visitors + if (is_array($result)) { + // Find each additional property + foreach (array_keys($result) as $key) { + // Check if the model actually knows this property. If so, then it is not additional + if (!$model->getProperty($key)) { + // Set the name to the key so that we can parse it with each visitor + $additional->setName($key); + $foundVisitors[$location]->visit($command, $response, $additional, $result); + } + } + // Reset the additionalProperties name to null + $additional->setName(null); + } + } + } +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php new file mode 100644 index 0000000..60b9334 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php @@ -0,0 +1,21 @@ +<?php + +namespace Guzzle\Service\Command; + +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Service\Command\CommandInterface; + +/** + * Translates command options and operation parameters into a request object + */ +interface RequestSerializerInterface +{ + /** + * Create a request for a command + * + * @param CommandInterface $command Command that will own the request + * + * @return RequestInterface + */ + public function prepare(CommandInterface $command); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php new file mode 100644 index 0000000..325dd08 --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php @@ -0,0 +1,18 @@ +<?php + +namespace Guzzle\Service\Command; + +/** + * Interface used to accept a completed OperationCommand and parse the result into a specific response type + */ +interface ResponseClassInterface +{ + /** + * Create a response model object from a completed command + * + * @param OperationCommand $command That serialized the request + * + * @return self + */ + public static function fromCommand(OperationCommand $command); +} diff --git a/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php new file mode 100644 index 0000000..015f0bb --- /dev/null +++ b/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php @@ -0,0 +1,18 @@ +<?php + +namespace Guzzle\Service\Command; + +/** + * Parses the HTTP response of a command and sets the appropriate result on a command object + */ +interface ResponseParserInterface +{ + /** + * Parse the HTTP response received by the command and update the command's result contents + * + * @param CommandInterface $command Command to parse and update + * + * @return mixed Returns the result to set on the command + */ + public function parse(CommandInterface $command); +} |