diff --git a/src/Guzzle/Service/AbstractConfigLoader.php b/src/Guzzle/Service/AbstractConfigLoader.php new file mode 100644 index 00000000..d8ad189b --- /dev/null +++ b/src/Guzzle/Service/AbstractConfigLoader.php @@ -0,0 +1,132 @@ +loadFile($config); + } elseif (!is_array($config)) { + throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config)); + } else { + $this->mergeIncludes($config); + } + + return $this->build($config, $options); + } + + /** + * Add an include alias to the loader + * + * @param string $filename Filename to alias (e.g. _foo) + * @param string $alias Actual file to use (e.g. /path/to/foo.json) + * + * @return self + */ + public function addAlias($filename, $alias) + { + $this->aliases[$filename] = $alias; + + return $this; + } + + /** + * Perform the parsing of a config file and create the end result + * + * @param array $config Configuration data + * @param array $options Options to use when building + * + * @return mixed + */ + protected abstract function build($config, array $options); + + /** + * Load a configuration file (can load JSON or PHP files that return an array when included) + * + * @param string $filename File to load + * + * @return array + * @throws InvalidArgumentException + */ + protected function loadFile($filename) + { + if (isset($this->aliases[$filename])) { + $filename = $this->aliases[$filename]; + } + + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + + $ext = pathinfo($filename, PATHINFO_EXTENSION); + if ($ext == 'js' || $ext == 'json') { + $config = json_decode(file_get_contents($filename), true); + // Throw an exception if there was an error loading the file + if ($error = json_last_error()) { + throw new JsonException("Error loading JSON data from {$filename}: {$error}"); + } + } elseif ($ext == 'php') { + $config = require $filename; + if (!is_array($config)) { + throw new InvalidArgumentException('PHP files must return an array of configuration data'); + } + } else { + throw new InvalidArgumentException('Unknown file extension: ' . $filename); + } + + // Merge include files into the configuration array + $this->mergeIncludes($config, dirname($filename)); + + return $config; + } + + /** + * Merges in all include files + * + * @param array $config Config data that contains includes + * @param string $basePath Base path to use when a relative path is encountered + * + * @return array Returns the merged and included data + */ + protected function mergeIncludes(&$config, $basePath = null) + { + if (!empty($config['includes'])) { + foreach ($config['includes'] as &$path) { + // Account for relative paths + if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) { + $path = "{$basePath}/{$path}"; + } + $config = $this->mergeData($this->loadFile($path), $config); + } + } + } + + /** + * Default implementation for merging two arrays of data (uses array_merge_recursive) + * + * @param array $a Original data + * @param array $b Data to merge into the original and overwrite existing values + * + * @return array + */ + protected function mergeData(array $a, array $b) + { + return array_merge_recursive($a, $b); + } +} diff --git a/src/Guzzle/Service/AbstractFactory.php b/src/Guzzle/Service/AbstractFactory.php deleted file mode 100644 index 3577903d..00000000 --- a/src/Guzzle/Service/AbstractFactory.php +++ /dev/null @@ -1,70 +0,0 @@ -getCacheTtlKey($config); - - // Check if a cache was provided - if (isset($options['cache.adapter']) && is_string($config)) { - $adapter = $options['cache.adapter']; - $ttl = isset($options[$cacheTtlKey]) ? $options[$cacheTtlKey] : 3600; - $cacheKey = 'guzzle' . crc32($config); - // Check if the instantiated data is in the cache - if ($cached = $adapter->fetch($cacheKey)) { - return $cached; - } - } - - // Get the name of the class to instantiate for the type of data - $factory = $this->getFactory($config); - if (!$factory || is_string($factory)) { - return $this->throwException($factory); - } - - $result = $factory->build($config, $options); - if ($adapter) { - $adapter->save($cacheKey, $result, $ttl); - } - - return $result; - } - - /** - * Get the string used to hold cache TTL specific information for the factory - * - * @param mixed $config Config data being loaded - * - * @return string - */ - abstract protected function getCacheTtlKey($config); - - /** - * Throw an exception when the abstract factory cannot instantiate anything - * - * @param string $message Message for the exception - * - * @return string - * @throws \Exception - */ - abstract protected function throwException($message = ''); - - /** - * Get a concrete factory based on the data provided - * - * @param mixed $config Data to use to determine the concrete factory - * - * @return mixed|string Returning a string will throw an exception with a specific message - */ - abstract protected function getFactory($config); -} diff --git a/src/Guzzle/Service/Builder/JsonServiceBuilderFactory.php b/src/Guzzle/Service/Builder/JsonServiceBuilderFactory.php deleted file mode 100644 index 11401ae6..00000000 --- a/src/Guzzle/Service/Builder/JsonServiceBuilderFactory.php +++ /dev/null @@ -1,41 +0,0 @@ -factory = $factory; - } - - /** - * {@inheritdoc} - */ - public function build($config, array $options = null) - { - return $this->factory->build($this->parseJsonFile($config), $options); - } - - /** - * {@inheritdoc} - * Adds special handling for JSON configuration merging - */ - protected function mergeJson(array $jsonA, array $jsonB) - { - return ServiceBuilderAbstractFactory::combineConfigs($jsonA, $jsonB); - } -} diff --git a/src/Guzzle/Service/Builder/ServiceBuilder.php b/src/Guzzle/Service/Builder/ServiceBuilder.php index 09e53e18..31819c79 100644 --- a/src/Guzzle/Service/Builder/ServiceBuilder.php +++ b/src/Guzzle/Service/Builder/ServiceBuilder.php @@ -4,7 +4,6 @@ namespace Guzzle\Service\Builder; use Guzzle\Common\AbstractHasDispatcher; use Guzzle\Http\ClientInterface; -use Guzzle\Service\Builder\ServiceBuilderAbstractFactory; use Guzzle\Service\Exception\ServiceBuilderException; use Guzzle\Service\Exception\ServiceNotFoundException; @@ -24,7 +23,7 @@ class ServiceBuilder extends AbstractHasDispatcher implements ServiceBuilderInte protected $clients = array(); /** - * @var ServiceBuilderAbstractFactory Cached instance of the abstract factory + * @var ServiceBuilderLoader Cached instance of the service builder loader */ protected static $cachedFactory; @@ -39,15 +38,15 @@ class ServiceBuilder extends AbstractHasDispatcher implements ServiceBuilderInte * @throws ServiceBuilderException if a file cannot be opened * @throws ServiceNotFoundException when trying to extend a missing client */ - public static function factory($config = null, array $globalParameters = null) + public static function factory($config = null, array $globalParameters = array()) { // @codeCoverageIgnoreStart if (!static::$cachedFactory) { - static::$cachedFactory = new ServiceBuilderAbstractFactory(); + static::$cachedFactory = new ServiceBuilderLoader(); } // @codeCoverageIgnoreEnd - return self::$cachedFactory->build($config, $globalParameters); + return self::$cachedFactory->load($config, $globalParameters); } /** diff --git a/src/Guzzle/Service/Builder/ServiceBuilderAbstractFactory.php b/src/Guzzle/Service/Builder/ServiceBuilderAbstractFactory.php deleted file mode 100644 index a69097cd..00000000 --- a/src/Guzzle/Service/Builder/ServiceBuilderAbstractFactory.php +++ /dev/null @@ -1,118 +0,0 @@ - &$service) { - - // By default, services completely override a previously defined service unless it extends itself - if (isset($a['services'][$name]['extends']) - && isset($b['services'][$name]['extends']) - && $b['services'][$name]['extends'] == $name - ) { - $service += $a['services'][$name]; - // Use the `extends` attribute of the parent - $service['extends'] = $a['services'][$name]['extends']; - // Merge parameters using a union if both have paramters - if (isset($a['services'][$name]['params'])) { - $service['params'] += $a['services'][$name]['params']; - } - } - } - } - - return $result; - } - - /** - * Get an array factory - * - * @return ArrayServiceBuilderFactory - */ - public function getArrayFactory() - { - if (!isset($this->factories['array'])) { - $this->factories['array'] = new ArrayServiceBuilderFactory(); - } - - return $this->factories['array']; - } - - /** - * Get a JSON factory - * - * @return JsonServiceBuilderFactory - */ - public function getJsonFactory() - { - if (!isset($this->factories['json'])) { - $this->factories['json'] = new JsonServiceBuilderFactory($this->getArrayFactory()); - } - - return $this->factories['json']; - } - - /** - * {@inheritdoc} - */ - protected function getFactory($config) - { - if (is_array($config)) { - return $this->getArrayFactory(); - } elseif (is_string($config)) { - $ext = pathinfo($config, PATHINFO_EXTENSION); - if ($ext == 'js' || $ext == 'json') { - return $this->getJsonFactory(); - } - } - - return 'Must pass the name of a .js or .json file or array'; - } - - /** - * {@inheritdoc} - */ - protected function getCacheTtlKey($config) - { - return 'cache.builder.ttl'; - } - - /** - * {@inheritdoc} - */ - protected function throwException($message = '') - { - throw new ServiceBuilderException($message ?: 'Unable to build service builder'); - } -} diff --git a/src/Guzzle/Service/Builder/ServiceBuilderFactoryInterface.php b/src/Guzzle/Service/Builder/ServiceBuilderFactoryInterface.php deleted file mode 100644 index 125d69b6..00000000 --- a/src/Guzzle/Service/Builder/ServiceBuilderFactoryInterface.php +++ /dev/null @@ -1,19 +0,0 @@ - &$service) { + + // By default, services completely override a previously defined service unless it extends itself + if (isset($a['services'][$name]['extends']) + && isset($b['services'][$name]['extends']) + && $b['services'][$name]['extends'] == $name + ) { + $service += $a['services'][$name]; + // Use the `extends` attribute of the parent + $service['extends'] = $a['services'][$name]['extends']; + // Merge parameters using a union if both have parameters + if (isset($a['services'][$name]['params'])) { + $service['params'] += $a['services'][$name]['params']; + } + } + } + } + + return $result; + } } diff --git a/src/Guzzle/Service/CachingConfigLoader.php b/src/Guzzle/Service/CachingConfigLoader.php new file mode 100644 index 00000000..db135ebb --- /dev/null +++ b/src/Guzzle/Service/CachingConfigLoader.php @@ -0,0 +1,55 @@ +loader = $loader; + $this->cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function load($config, array $options = array()) + { + if (is_string($config)) { + $key = 'loader_' . crc32($config); + if ($result = $this->cache->fetch($key)) { + return $result; + } + } else { + $key = false; + } + + $result = $this->loader->load($config, $options); + if ($key) { + $this->cache->save($key, $result); + } + + return $result; + } +} diff --git a/src/Guzzle/Service/ConfigLoaderInterface.php b/src/Guzzle/Service/ConfigLoaderInterface.php new file mode 100644 index 00000000..304100dc --- /dev/null +++ b/src/Guzzle/Service/ConfigLoaderInterface.php @@ -0,0 +1,22 @@ +loader) { - $this->loader = new JsonLoader(); - } - - return ServiceDescription::factory($this->loader->parseJsonFile($config)); - } -} diff --git a/src/Guzzle/Service/Description/ServiceDescription.php b/src/Guzzle/Service/Description/ServiceDescription.php index a5ec4a31..0931e0b0 100644 --- a/src/Guzzle/Service/Description/ServiceDescription.php +++ b/src/Guzzle/Service/Description/ServiceDescription.php @@ -38,24 +38,24 @@ class ServiceDescription implements ServiceDescriptionInterface protected $extraData = array(); /** - * @var ServiceDescriptionFactoryInterface Factory used in factory method + * @var ServiceDescriptionLoader Factory used in factory method */ - protected static $descriptionFactory; + protected static $descriptionLoader; /** * {@inheritdoc} * @param string|array $config File to build or array of operation information * @param array $options Service description factory options */ - public static function factory($config, array $options = null) + public static function factory($config, array $options = array()) { // @codeCoverageIgnoreStart - if (!self::$descriptionFactory) { - self::$descriptionFactory = new ServiceDescriptionAbstractFactory(); + if (!self::$descriptionLoader) { + self::$descriptionLoader = new ServiceDescriptionLoader(); } // @codeCoverageIgnoreEnd - return self::$descriptionFactory->build($config, $options); + return self::$descriptionLoader->load($config, $options); } /** diff --git a/src/Guzzle/Service/Description/ServiceDescriptionAbstractFactory.php b/src/Guzzle/Service/Description/ServiceDescriptionAbstractFactory.php deleted file mode 100644 index 2e6645b0..00000000 --- a/src/Guzzle/Service/Description/ServiceDescriptionAbstractFactory.php +++ /dev/null @@ -1,43 +0,0 @@ -aliases[$filename] = $alias; - - return $this; - } - - /** - * Loads a JSON file and includes any files in the "includes" array - * - * @param string $jsonFile File to load - * - * @return array - * @throws JsonException if unable to open the file or if there is a parsing error - */ - public function parseJsonFile($jsonFile) - { - // Use the registered alias if one matches the file - if (isset($this->aliases[$jsonFile])) { - $jsonFile = $this->aliases[$jsonFile]; - } - - // Ensure that the file can be opened for reading - if (!is_readable($jsonFile)) { - throw new JsonException("Unable to open {$jsonFile} for reading"); - } - - $data = json_decode(file_get_contents($jsonFile), true); - // Throw an exception if there was an error loading the file - if ($error = json_last_error()) { - throw new JsonException("Error loading JSON data from {$jsonFile}: {$error}"); - } - - // Handle includes - if (!empty($data['includes'])) { - foreach ($data['includes'] as $path) { - if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path])) { - $path = dirname($jsonFile) . DIRECTORY_SEPARATOR . $path; - } - $data = $this->mergeJson($this->parseJsonFile($path), $data); - } - } - - return $data; - } - - /** - * Default implementation for merging two JSON files (uses array_merge_recursive) - * - * @param array $a Original JSON data - * @param array $b JSON data to merge into the original and overwrite existing values - * - * @return array - */ - protected function mergeJson(array $a, array $b) - { - return array_merge_recursive($a, $b); - } -} diff --git a/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php b/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php new file mode 100644 index 00000000..427c3396 --- /dev/null +++ b/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php @@ -0,0 +1,128 @@ +loader = $this->getMockBuilder('Guzzle\Service\AbstractConfigLoader') + ->setMethods(array('build')) + ->getMockForAbstractClass(); + } + + public function tearDown() + { + foreach ($this->cleanup as $file) { + unlink($file); + } + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnlyLoadsSupportedTypes() + { + $this->loader->load(new \stdClass()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open fooooooo! for reading + */ + public function testFileMustBeReadable() + { + $this->loader->load('fooooooo!'); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unknown file extension + */ + public function testMustBeSupportedExtension() + { + $this->loader->load(dirname(__DIR__) . '/TestData/FileBody.txt'); + } + + /** + * @expectedException Guzzle\Service\Exception\JsonException + * @expectedExceptionMessage Error loading JSON data from + */ + public function testJsonMustBeValue() + { + $filename = tempnam(sys_get_temp_dir(), 'json') . '.json'; + file_put_contents($filename, '{/{./{}foo'); + $this->cleanup[] = $filename; + $this->loader->load($filename); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage PHP files must return an array + */ + public function testPhpFilesMustReturnAnArray() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, 'cleanup[] = $filename; + $this->loader->load($filename); + } + + public function testLoadsPhpFileIncludes() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, ' "bar");'); + $this->cleanup[] = $filename; + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $config = $this->loader->load($filename); + $this->assertEquals(array('foo' => 'bar'), $config); + } + + public function testCanCreateFromJson() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($file); + // Ensure that the config files were merged using the includes directives + $this->assertArrayHasKey('includes', $data); + $this->assertArrayHasKey('services', $data); + $this->assertInternalType('array', $data['services']['foo']); + $this->assertInternalType('array', $data['services']['abstract']); + $this->assertInternalType('array', $data['services']['mock']); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testUsesAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo', $file); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load('foo'); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testCanLoadArraysWithIncludes() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $config = array('includes' => array($file)); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($config); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } +} diff --git a/tests/Guzzle/Tests/Service/AbstractFactoryTest.php b/tests/Guzzle/Tests/Service/AbstractFactoryTest.php deleted file mode 100644 index 91503ce9..00000000 --- a/tests/Guzzle/Tests/Service/AbstractFactoryTest.php +++ /dev/null @@ -1,64 +0,0 @@ -getMockBuilder('Guzzle\Service\AbstractFactory') - ->setMethods(array('getCacheTtlKey', 'throwException', 'getFactory')) - ->getMockForAbstractClass(); - } - - public function testCachesArtifacts() - { - $jsonFile = __DIR__ . '/../TestData/test_service.json'; - - $adapter = new DoctrineCacheAdapter(new ArrayCache()); - $factory = $this->getFactory(); - - $factory->expects($this->once()) - ->method('getFactory') - ->will($this->returnValue(new JsonServiceBuilderFactory(new ArrayServiceBuilderFactory()))); - - // Create a service and add it to the cache - $service = $factory->build($jsonFile, array( - 'cache.adapter' => $adapter - )); - - // Ensure the cache key was set - $this->assertTrue($adapter->contains('guzzle' . crc32($jsonFile))); - - // Grab the service from the cache - $this->assertEquals($service, $factory->build($jsonFile, array( - 'cache.adapter' => $adapter - ))); - } - - /** - * @expectedException Guzzle\Service\Exception\ServiceBuilderException - */ - public function testThrowsExceptionsWhenNoFactoryResolves() - { - $factory = $this->getFactory(); - $factory->expects($this->any()) - ->method('getFactory') - ->will($this->returnValue(false)); - $factory->expects($this->any()) - ->method('throwException') - ->will($this->throwException(new ServiceBuilderException())); - - $service = $factory->build('foo'); - } -} diff --git a/tests/Guzzle/Tests/Service/Builder/ArrayServiceBuilderFactoryTest.php b/tests/Guzzle/Tests/Service/Builder/ArrayServiceBuilderFactoryTest.php deleted file mode 100644 index de942af3..00000000 --- a/tests/Guzzle/Tests/Service/Builder/ArrayServiceBuilderFactoryTest.php +++ /dev/null @@ -1,107 +0,0 @@ - array( - 'abstract' => array( - 'params' => array( - 'access_key' => 'xyz', - 'secret' => 'abc', - ), - ), - 'foo' => array( - 'extends' => 'abstract', - 'params' => array( - 'baz' => 'bar', - ), - ), - 'mock' => array( - 'extends' => 'abstract', - 'params' => array( - 'username' => 'foo', - 'password' => 'baz', - 'subdomain' => 'bar', - ) - ) - ) - ); - - $builder = $arrayFactory->build($data); - - // Ensure that services were parsed - $this->assertTrue(isset($builder['mock'])); - $this->assertTrue(isset($builder['abstract'])); - $this->assertTrue(isset($builder['foo'])); - $this->assertFalse(isset($builder['jimmy'])); - } - - /** - * @expectedException Guzzle\Service\Exception\ServiceNotFoundException - * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract - */ - public function testThrowsExceptionWhenExtendingNonExistentService() - { - $arrayFactory = new ArrayServiceBuilderFactory(); - - $data = array( - 'services' => array( - 'foo' => array( - 'extends' => 'abstract' - ) - ) - ); - - $builder = $arrayFactory->build($data); - } - - public function testAllowsGlobalParameterOverrides() - { - $arrayFactory = new ArrayServiceBuilderFactory(); - - $data = array( - 'services' => array( - 'foo' => array( - 'params' => array( - 'foo' => 'baz', - 'bar' => 'boo' - ) - ) - ) - ); - - $builder = $arrayFactory->build($data, array( - 'bar' => 'jar', - 'far' => 'car' - )); - - $compiled = json_decode($builder->serialize(), true); - $this->assertEquals(array( - 'foo' => 'baz', - 'bar' => 'jar', - 'far' => 'car' - ), $compiled['foo']['params']); - } - - public function tstDoesNotErrorOnCircularReferences() - { - $arrayFactory = new ArrayServiceBuilderFactory(); - $arrayFactory->build(array( - 'services' => array( - 'too' => array('extends' => 'ball'), - 'ball' => array('extends' => 'too'), - ) - )); - } -} diff --git a/tests/Guzzle/Tests/Service/Builder/JsonServiceBuilderFactoryTest.php b/tests/Guzzle/Tests/Service/Builder/JsonServiceBuilderFactoryTest.php deleted file mode 100644 index 40954ed2..00000000 --- a/tests/Guzzle/Tests/Service/Builder/JsonServiceBuilderFactoryTest.php +++ /dev/null @@ -1,86 +0,0 @@ -build($file); - // Build it again, get a similar result using the same JsonLoader - $this->assertEquals($builder, $j->build($file)); - - // Ensure that services were parsed - $this->assertTrue(isset($builder['mock'])); - $this->assertTrue(isset($builder['abstract'])); - $this->assertTrue(isset($builder['foo'])); - $this->assertFalse(isset($builder['jimmy'])); - } - - public function testServicesCanBeExtendedWithIncludes() - { - $data = array( - 'services' => array( - 'foo' => array( - 'class' => 'stdClass', - 'params' => array('baz' => 'bar') - ), - 'bar' => array( - 'extends' => 'foo', - 'params' => array( - 'test' => '123', - 'ext' => 'abc' - ) - ) - ) - ); - $file1 = tempnam(sys_get_temp_dir(), 'service_1') . '.json'; - file_put_contents($file1, json_encode($data)); - - $data = array( - 'includes' => array($file1), - 'services' => array( - 'bar' => array( - 'extends' => 'bar', - 'params' => array( - 'test' => '456' - ) - ) - ) - ); - $file2 = tempnam(sys_get_temp_dir(), 'service_2') . '.json'; - file_put_contents($file2, json_encode($data)); - - $builder = ServiceBuilder::factory($file2); - unlink($file1); - unlink($file2); - - $this->assertEquals(array( - 'bar' => array( - 'class' => 'stdClass', - 'extends' => 'foo', - 'params' => array( - 'test' => '456', - 'baz' => 'bar', - 'ext' => 'abc' - ) - ), - 'foo' => array( - 'class' => 'stdClass', - 'params' => array('baz' => 'bar') - ) - ), $this->readAttribute($builder, 'builderConfig')); - } -} diff --git a/tests/Guzzle/Tests/Service/Builder/ServiceBuilderAbstractFactoryTest.php b/tests/Guzzle/Tests/Service/Builder/ServiceBuilderAbstractFactoryTest.php deleted file mode 100644 index cc077ae7..00000000 --- a/tests/Guzzle/Tests/Service/Builder/ServiceBuilderAbstractFactoryTest.php +++ /dev/null @@ -1,111 +0,0 @@ -jsonFile = __DIR__ . '/../../TestData/services/json1.json'; - } - - public function testFactoryDelegatesToConcreteFactories() - { - $factory = new ServiceBuilderAbstractFactory(); - $this->assertInstanceOf('Guzzle\Service\Builder\ServiceBuilder', $factory->build($this->jsonFile)); - } - - /** - * @expectedException Guzzle\Service\Exception\ServiceBuilderException - * @expectedExceptionMessage Must pass the name of a .js or .json file or array - */ - public function testThrowsExceptionWhenInvalidFileExtensionIsPassed() - { - $factory = new ServiceBuilderAbstractFactory(); - $factory->build(__FILE__); - } - - /** - * @expectedException Guzzle\Service\Exception\ServiceBuilderException - * @expectedExceptionMessage Must pass the name of a .js or .json file or array - */ - public function testThrowsExceptionWhenUnknownTypeIsPassed() - { - $factory = new ServiceBuilderAbstractFactory(); - $factory->build(new \stdClass()); - } - - public function configProvider() - { - $foo = array( - 'extends' => 'bar', - 'class' => 'stdClass', - 'params' => array('a' => 'test', 'b' => '456') - ); - - return array( - array( - // Does not extend the existing `foo` service but overwrites it - array( - 'services' => array( - 'foo' => $foo, - 'bar' => array('params' => array('baz' => '123')) - ) - ), - array( - 'services' => array( - 'foo' => array('class' => 'Baz') - ) - ), - array( - 'services' => array( - 'foo' => array('class' => 'Baz'), - 'bar' => array('params' => array('baz' => '123')) - ) - ) - ), - array( - // Extends the existing `foo` service - array( - 'services' => array( - 'foo' => $foo, - 'bar' => array('params' => array('baz' => '123')) - ) - ), - array( - 'services' => array( - 'foo' => array( - 'extends' => 'foo', - 'params' => array('b' => '123', 'c' => 'def') - ) - ) - ), - array( - 'services' => array( - 'foo' => array( - 'extends' => 'bar', - 'class' => 'stdClass', - 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def') - ), - 'bar' => array('params' => array('baz' => '123')) - ) - ) - ) - ); - } - - /** - * @dataProvider configProvider - */ - public function testCombinesConfigs($a, $b, $c) - { - $this->assertEquals($c, ServiceBuilderAbstractFactory::combineConfigs($a, $b)); - } -} diff --git a/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php b/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php new file mode 100644 index 00000000..f63070e3 --- /dev/null +++ b/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php @@ -0,0 +1,177 @@ + array( + 'abstract' => array( + 'params' => array( + 'access_key' => 'xyz', + 'secret' => 'abc', + ), + ), + 'foo' => array( + 'extends' => 'abstract', + 'params' => array( + 'baz' => 'bar', + ), + ), + 'mock' => array( + 'extends' => 'abstract', + 'params' => array( + 'username' => 'foo', + 'password' => 'baz', + 'subdomain' => 'bar', + ) + ) + ) + ); + + $builder = $arrayFactory->load($data); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract + */ + public function testThrowsExceptionWhenExtendingNonExistentService() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'extends' => 'abstract' + ) + ) + ); + + $builder = $arrayFactory->load($data); + } + + public function testAllowsGlobalParameterOverrides() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'params' => array( + 'foo' => 'baz', + 'bar' => 'boo' + ) + ) + ) + ); + + $builder = $arrayFactory->load($data, array( + 'bar' => 'jar', + 'far' => 'car' + )); + + $compiled = json_decode($builder->serialize(), true); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'jar', + 'far' => 'car' + ), $compiled['foo']['params']); + } + + public function tstDoesNotErrorOnCircularReferences() + { + $arrayFactory = new ServiceBuilderLoader(); + $arrayFactory->load(array( + 'services' => array( + 'too' => array('extends' => 'ball'), + 'ball' => array('extends' => 'too'), + ) + )); + } + + public function configProvider() + { + $foo = array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '456') + ); + + return array( + array( + // Does not extend the existing `foo` service but overwrites it + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz') + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz'), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ), + array( + // Extends the existing `foo` service + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'foo', + 'params' => array('b' => '123', 'c' => 'def') + ) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def') + ), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ) + ); + } + + /** + * @dataProvider configProvider + */ + public function testCombinesConfigs($a, $b, $c) + { + $l = new ServiceBuilderLoader(); + $m = new \ReflectionMethod($l, 'mergeData'); + $m->setAccessible(true); + $this->assertEquals($c, $m->invoke($l, $a, $b)); + } +} diff --git a/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php b/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php index ee70930a..f8bd5ca2 100644 --- a/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php +++ b/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php @@ -8,6 +8,9 @@ use Guzzle\Service\Client; use Guzzle\Service\Exception\ServiceNotFoundException; use Doctrine\Common\Cache\ArrayCache; +/** + * @covers Guzzle\Service\Builder\ServiceBuilder + */ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase { protected $arrayData = array( @@ -54,10 +57,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase ) ); - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::serialize - * @covers Guzzle\Service\Builder\ServiceBuilder::unserialize - */ public function testAllowsSerialization() { $builder = ServiceBuilder::factory($this->arrayData); @@ -65,9 +64,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertEquals($cached, $builder); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - */ public function testDelegatesFactoryMethodToAbstractFactory() { $builder = ServiceBuilder::factory($this->arrayData); @@ -76,7 +72,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase } /** - * @covers Guzzle\Service\Builder\ServiceBuilder::get * @expectedException Guzzle\Service\Exception\ServiceNotFoundException * @expectedExceptionMessage No service is registered as foobar */ @@ -85,9 +80,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase ServiceBuilder::factory($this->arrayData)->get('foobar'); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::get - */ public function testStoresClientCopy() { $builder = ServiceBuilder::factory($this->arrayData); @@ -114,9 +106,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertFalse($client2 === $builder->get('billy.mock')); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder - */ public function testBuildersPassOptionsThroughToClients() { $s = new ServiceBuilder(array( @@ -136,9 +125,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport')); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder - */ public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified() { $s = new ServiceBuilder(array( @@ -158,13 +144,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $c); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::set - * @covers Guzzle\Service\Builder\ServiceBuilder::offsetSet - * @covers Guzzle\Service\Builder\ServiceBuilder::offsetGet - * @covers Guzzle\Service\Builder\ServiceBuilder::offsetUnset - * @covers Guzzle\Service\Builder\ServiceBuilder::offsetExists - */ public function testUsedAsArray() { $b = ServiceBuilder::factory($this->arrayData); @@ -179,9 +158,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertInstanceOf('Guzzle\\Service\\Client', $b['michael.mock']); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - */ public function testFactoryCanCreateFromJson() { $tmp = sys_get_temp_dir() . '/test.js'; @@ -193,9 +169,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertEquals('billy', $s->getConfig('username')); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - */ public function testFactoryCanCreateFromArray() { $b = ServiceBuilder::factory($this->arrayData); @@ -204,36 +177,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertEquals('billy', $s->getConfig('username')); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - * @expectedException Guzzle\Service\Exception\ServiceBuilderException - * @expectedExceptionMessage Must pass the name of a .js or .json file or array - */ - public function testFactoryValidatesFileExtension() - { - $tmp = sys_get_temp_dir() . '/test.abc'; - file_put_contents($tmp, 'data'); - try { - ServiceBuilder::factory($tmp); - } catch (\RuntimeException $e) { - unlink($tmp); - throw $e; - } - } - - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - * @expectedException Guzzle\Service\Exception\ServiceBuilderException - * @expectedExceptionMessage Must pass the name of a .js or .json file or array - */ - public function testFactoryValidatesObjectTypes() - { - ServiceBuilder::factory(new \stdClass()); - } - - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - */ public function testFactoryDoesNotRequireParams() { $b = ServiceBuilder::factory($this->arrayData); @@ -241,9 +184,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertEquals('billy', $s->getConfig('username')); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder - */ public function testBuilderAllowsReferencesBetweenClients() { $builder = ServiceBuilder::factory(array( @@ -272,10 +212,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertEquals('1', $builder['b']->getConfig('username')); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::getAllEvents - * @covers Guzzle\Service\Builder\ServiceBuilder::get - */ public function testEmitsEventsWhenClientsAreCreated() { // Ensure that the client signals that it emits an event @@ -307,9 +243,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); } - /** - * @covers Guzzle\Service\Builder\ServiceBuilder::factory - */ public function testCanAddGlobalParametersToServicesOnLoad() { $builder = ServiceBuilder::factory($this->arrayData, array( @@ -325,17 +258,6 @@ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase } } - public function testDescriptionIsCacheable() - { - $jsonFile = __DIR__ . '/../../TestData/test_service.json'; - $adapter = new DoctrineCacheAdapter(new ArrayCache()); - $builder = ServiceBuilder::factory($jsonFile, array('cache.adapter' => $adapter)); - // Ensure the cache key was set - $this->assertTrue($adapter->contains('guzzle' . crc32($jsonFile))); - // Grab the service from the cache - $this->assertEquals($builder, ServiceBuilder::factory($jsonFile, array('cache.adapter' => $adapter))); - } - public function testCacheServiceCanBeCreatedAndInjectedIntoOtherServices() { $builder = ServiceBuilder::factory($this->arrayData); diff --git a/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php b/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php new file mode 100644 index 00000000..b8245ad5 --- /dev/null +++ b/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php @@ -0,0 +1,43 @@ +getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->once()) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load('foo')); + $this->assertEquals($data, $cache->load('foo')); + } + + public function testDoesNotCacheArrays() + { + $cache = new DoctrineCacheAdapter(new ArrayCache()); + $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->exactly(2)) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load(array())); + $this->assertEquals($data, $cache->load(array())); + } +} diff --git a/tests/Guzzle/Tests/Service/Command/CommandTest.php b/tests/Guzzle/Tests/Service/Command/CommandTest.php index b1510e75..a2adee00 100644 --- a/tests/Guzzle/Tests/Service/Command/CommandTest.php +++ b/tests/Guzzle/Tests/Service/Command/CommandTest.php @@ -410,6 +410,16 @@ class CommandTest extends AbstractCommandTest $command->prepare(); } + public function testValidatorUpdatesCommand() + { + $command = new MockCommand(array('test' => 123, 'foo' => 'bar')); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('abc', $command->get('_internal')); + $this->assertEquals('BAR', $command->get('foo')); + } + /** * @expectedException Guzzle\Service\Exception\ValidationException * @expectedExceptionMessage [Foo] Baz diff --git a/tests/Guzzle/Tests/Service/Description/JsonDescriptionBuilderTest.php b/tests/Guzzle/Tests/Service/Description/JsonDescriptionBuilderTest.php deleted file mode 100644 index 0f4c3c85..00000000 --- a/tests/Guzzle/Tests/Service/Description/JsonDescriptionBuilderTest.php +++ /dev/null @@ -1,31 +0,0 @@ -build('/foo.does.not.exist'); - } - - public function testBuildsServiceDescriptions() - { - $j = new JsonDescriptionBuilder(); - $description = $j->build(__DIR__ . '/../../TestData/test_service.json'); - $this->assertTrue($description->hasOperation('test')); - $test = $description->getOperation('test'); - $this->assertEquals('/path', $test->getUri()); - $test = $description->getOperation('concrete'); - $this->assertEquals('/abstract', $test->getUri()); - } -} diff --git a/tests/Guzzle/Tests/Service/Description/OperationTest.php b/tests/Guzzle/Tests/Service/Description/OperationTest.php index cb549d95..90c4cd6e 100644 --- a/tests/Guzzle/Tests/Service/Description/OperationTest.php +++ b/tests/Guzzle/Tests/Service/Description/OperationTest.php @@ -260,12 +260,13 @@ class OperationTest extends \Guzzle\Tests\GuzzleTestCase public function testHasResponseType() { - $o = new Operation(); - $this->assertEquals('primitive', $o->getResponseType()); - $this->assertEquals('model', $o->setResponseType('model')->getResponseType()); // infers in the constructor $o = new Operation(array('responseClass' => 'array')); $this->assertEquals('primitive', $o->getResponseType()); + // Infers when set + $o = new Operation(); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('model', $o->setResponseType('model')->getResponseType()); } /** diff --git a/tests/Guzzle/Tests/Service/Description/ServiceDescriptionAbstractFactoryTest.php b/tests/Guzzle/Tests/Service/Description/ServiceDescriptionAbstractFactoryTest.php deleted file mode 100644 index 44879e5f..00000000 --- a/tests/Guzzle/Tests/Service/Description/ServiceDescriptionAbstractFactoryTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertInstanceOf( - 'Guzzle\Service\Description\ServiceDescription', - $factory->build(__DIR__ . '/../../TestData/test_service.json') - ); - } - - /** - * @expectedException Guzzle\Service\Exception\DescriptionBuilderException - */ - public function testFactoryEnsuresItCanHandleTheTypeOfFileOrArray() - { - $factory = new ServiceDescriptionAbstractFactory(); - $factory->build('jarJarBinks'); - } -} diff --git a/tests/Guzzle/Tests/Service/Description/ArrayDescriptionBuilderTest.php b/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php similarity index 87% rename from tests/Guzzle/Tests/Service/Description/ArrayDescriptionBuilderTest.php rename to tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php index 93f48a96..0cfd0d3f 100644 --- a/tests/Guzzle/Tests/Service/Description/ArrayDescriptionBuilderTest.php +++ b/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php @@ -3,17 +3,13 @@ namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\ServiceDescription; -use Guzzle\Service\Description\ArrayDescriptionBuilder; +use Guzzle\Service\Description\ServiceDescriptionLoader; /** - * @covers Guzzle\Service\Description\ArrayDescriptionBuilder + * @covers Guzzle\Service\Description\ServiceDescriptionLoader */ -class ArrayDescriptionBuilderTest extends \Guzzle\Tests\GuzzleTestCase +class ServiceDescriptionLoaderTest extends \Guzzle\Tests\GuzzleTestCase { - /** - * @covers Guzzle\Service\Description\ServiceDescription::factory - * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build - */ public function testAllowsDeepNestedInheritance() { $d = ServiceDescription::factory(array( @@ -39,8 +35,6 @@ class ArrayDescriptionBuilderTest extends \Guzzle\Tests\GuzzleTestCase } /** - * @covers Guzzle\Service\Description\ServiceDescription::factory - * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build * @expectedException RuntimeException */ public function testThrowsExceptionWhenExtendingMissingCommand() diff --git a/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php b/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php index 89044468..5f878571 100644 --- a/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php +++ b/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php @@ -6,6 +6,9 @@ use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Description\Operation; use Guzzle\Service\Client; +/** + * @covers Guzzle\Service\Description\ServiceDescription + */ class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase { protected $serviceData; @@ -28,7 +31,7 @@ class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase /** * @covers Guzzle\Service\Description\ServiceDescription::factory - * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build + * @covers Guzzle\Service\Description\ServiceDescriptionLoader::build */ public function testFactoryDelegatesToConcreteFactories() { @@ -36,12 +39,6 @@ class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile)); } - /** - * @covers Guzzle\Service\Description\ServiceDescription - * @covers Guzzle\Service\Description\ServiceDescription::__construct - * @covers Guzzle\Service\Description\ServiceDescription::getOperations - * @covers Guzzle\Service\Description\ServiceDescription::getOperation - */ public function testConstructor() { $service = new ServiceDescription(array('operations' => $this->serviceData)); @@ -50,10 +47,6 @@ class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase $this->assertTrue($service->hasOperation('test_command')); } - /** - * @covers Guzzle\Service\Description\ServiceDescription::serialize - * @covers Guzzle\Service\Description\ServiceDescription::unserialize - */ public function testIsSerializable() { $service = new ServiceDescription(array('operations' => $this->serviceData)); diff --git a/tests/Guzzle/Tests/Service/JsonLoaderTest.php b/tests/Guzzle/Tests/Service/JsonLoaderTest.php deleted file mode 100644 index dbf63c45..00000000 --- a/tests/Guzzle/Tests/Service/JsonLoaderTest.php +++ /dev/null @@ -1,54 +0,0 @@ -parseJsonFile('fooooooo!'); - } - - /** - * @expectedException Guzzle\Service\Exception\JsonException - * @expectedExceptionMessage Error loading JSON data from - */ - public function testJsonMustBeValue() - { - $loader = new JsonLoader(); - $loader->parseJsonFile(__FILE__); - } - - public function testFactoryCanCreateFromJson() - { - $file = dirname(__DIR__) . '/TestData/services/json1.json'; - $loader = new JsonLoader(); - $data = $loader->parseJsonFile($file); - - $this->assertArrayHasKey('includes', $data); - $this->assertArrayHasKey('services', $data); - $this->assertInternalType('array', $data['services']['foo']); - $this->assertInternalType('array', $data['services']['abstract']); - $this->assertInternalType('array', $data['services']['mock']); - $this->assertEquals('bar', $data['services']['foo']['params']['baz']); - } - - public function testFactoryUsesAliases() - { - $file = dirname(__DIR__) . '/TestData/services/json1.json'; - $loader = new JsonLoader(); - $loader->addAlias('foo', $file); - $data = $loader->parseJsonFile('foo'); - $this->assertEquals('bar', $data['services']['foo']['params']['baz']); - } -} diff --git a/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php b/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php index 193f2ece..831a7e79 100644 --- a/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php +++ b/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php @@ -19,7 +19,8 @@ class MockCommand extends \Guzzle\Service\Command\AbstractCommand ), '_internal' => array( 'default' => 'abc' - ) + ), + 'foo' => array('filters' => array('strtoupper')) ) )); }