From d427f9dff7e824a506e7b1728d6efed96e5957a1 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Thu, 29 Mar 2012 17:57:47 +0200 Subject: [PATCH 1/2] Json reader/writer for Zend\Config --- src/Reader/Json.php | 113 ++++++++++++++++++ src/Writer/Json.php | 43 +++++++ test/Reader/JsonTest.php | 83 +++++++++++++ test/Reader/TestAssets/Json/include-base.json | 3 + .../TestAssets/Json/include-base_nested.json | 3 + .../TestAssets/Json/include-target.json | 3 + test/Reader/TestAssets/Json/invalid.json | 3 + test/Writer/JsonTest.php | 70 +++++++++++ test/Writer/files/allsections.json | 45 +++++++ 9 files changed, 366 insertions(+) create mode 100644 src/Reader/Json.php create mode 100644 src/Writer/Json.php create mode 100644 test/Reader/JsonTest.php create mode 100644 test/Reader/TestAssets/Json/include-base.json create mode 100644 test/Reader/TestAssets/Json/include-base_nested.json create mode 100644 test/Reader/TestAssets/Json/include-target.json create mode 100644 test/Reader/TestAssets/Json/invalid.json create mode 100644 test/Writer/JsonTest.php create mode 100644 test/Writer/files/allsections.json diff --git a/src/Reader/Json.php b/src/Reader/Json.php new file mode 100644 index 0000000..b559a88 --- /dev/null +++ b/src/Reader/Json.php @@ -0,0 +1,113 @@ +directory = dirname($filename); + + try { + $config = JsonFormat::decode(file_get_contents($filename), JsonFormat::TYPE_ARRAY); + } catch (\Zend\Json\Exception\RuntimeException $e) { + throw new Exception\RuntimeException($e->getMessage()); + } + + return $this->process($config); + } + + /** + * fromString(): defined by Reader interface. + * + * @see Reader::fromString() + * @param string $string + * @return array + */ + public function fromString($string) + { + if (empty($string)) { + return array(); + } + $this->directory = null; + + try { + $config = JsonFormat::decode($string, JsonFormat::TYPE_ARRAY); + } catch (\Zend\Json\Exception\RuntimeException $e) { + throw new Exception\RuntimeException($e->getMessage()); + } + + return $this->process($config); + } + /** + * Process the array for @include + * + * @param array $data + * @return array + */ + protected function process(array $data) { + foreach ($data as $key => $value) { + if (is_array($value)) { + $data[$key] = $this->process($value); + } + if (trim($key)==='@include') { + if ($this->directory === null) { + throw new Exception\RuntimeException('Cannot process @include statement for a json string'); + } + $reader = clone $this; + unset($data[$key]); + $data = array_replace_recursive($data, $reader->fromFile($this->directory . '/' . $value)); + } + } + return $data; + } +} \ No newline at end of file diff --git a/src/Writer/Json.php b/src/Writer/Json.php new file mode 100644 index 0000000..5020f5b --- /dev/null +++ b/src/Writer/Json.php @@ -0,0 +1,43 @@ +reader = new Json(); + } + + /** + * getTestAssetPath(): defined by AbstractReaderTestCase. + * + * @see AbstractReaderTestCase::getTestAssetPath() + * @return string + */ + protected function getTestAssetPath($name) + { + return __DIR__ . '/TestAssets/Json/' . $name . '.json'; + } + + public function testInvalidJsonFile() + { + $this->setExpectedException('Zend\Config\Exception\RuntimeException'); + $arrayJson = $this->reader->fromFile($this->getTestAssetPath('invalid')); + } + + public function testIncludeAsElement() + { + $arrayJson = $this->reader->fromFile($this->getTestAssetPath('include-base_nested')); + $this->assertEquals($arrayJson['bar']['foo'], 'foo'); + } + + public function testFromString() + { + $json = '{ "test" : "foo", "bar" : [ "baz", "foo" ] }'; + + $arrayJson = $this->reader->fromString($json); + + $this->assertEquals($arrayJson['test'], 'foo'); + $this->assertEquals($arrayJson['bar'][0], 'baz'); + $this->assertEquals($arrayJson['bar'][1], 'foo'); + } + + public function testInvalidString() + { + $json = '{"foo":"bar"'; + + $this->setExpectedException('Zend\Config\Exception\RuntimeException'); + $arrayIni = $this->reader->fromString($json); + } + +} diff --git a/test/Reader/TestAssets/Json/include-base.json b/test/Reader/TestAssets/Json/include-base.json new file mode 100644 index 0000000..ee3af2e --- /dev/null +++ b/test/Reader/TestAssets/Json/include-base.json @@ -0,0 +1,3 @@ +{ + "@include" : "include-target.json" +} \ No newline at end of file diff --git a/test/Reader/TestAssets/Json/include-base_nested.json b/test/Reader/TestAssets/Json/include-base_nested.json new file mode 100644 index 0000000..7ee7b59 --- /dev/null +++ b/test/Reader/TestAssets/Json/include-base_nested.json @@ -0,0 +1,3 @@ +{ + "bar" : { "@include" : "include-target.json" } +} \ No newline at end of file diff --git a/test/Reader/TestAssets/Json/include-target.json b/test/Reader/TestAssets/Json/include-target.json new file mode 100644 index 0000000..67e4d41 --- /dev/null +++ b/test/Reader/TestAssets/Json/include-target.json @@ -0,0 +1,3 @@ +{ + "foo" : "foo" +} diff --git a/test/Reader/TestAssets/Json/invalid.json b/test/Reader/TestAssets/Json/invalid.json new file mode 100644 index 0000000..3e1b208 --- /dev/null +++ b/test/Reader/TestAssets/Json/invalid.json @@ -0,0 +1,3 @@ +{ + "test": "test" + diff --git a/test/Writer/JsonTest.php b/test/Writer/JsonTest.php new file mode 100644 index 0000000..ed89f00 --- /dev/null +++ b/test/Writer/JsonTest.php @@ -0,0 +1,70 @@ +reader = new JsonReader(); + $this->writer = new JsonWriter(); + } + + + + public function testNoSection() + { + $config = new Config(array('test' => 'foo', 'test2' => array('test3' => 'bar'))); + + $this->writer->toFile($this->getTestAssetFileName(), $config); + + $config = $this->reader->fromFile($this->getTestAssetFileName()); + + $this->assertEquals('foo', $config['test']); + $this->assertEquals('bar', $config['test2']['test3']); + } + + public function testWriteAndReadOriginalFile() + { + $config = $this->reader->fromFile(__DIR__ . '/files/allsections.json'); + + $this->writer->toFile($this->getTestAssetFileName(), $config); + + $config = $this->reader->fromFile($this->getTestAssetFileName()); + + $this->assertEquals('multi', $config['all']['one']['two']['three']); + + } +} diff --git a/test/Writer/files/allsections.json b/test/Writer/files/allsections.json new file mode 100644 index 0000000..d47bd4e --- /dev/null +++ b/test/Writer/files/allsections.json @@ -0,0 +1,45 @@ +{ +"all" : { + "hostname" : "all", + "name" : "thisname", + "db" : { + "host" : "127.0.0.1", + "user" : "username", + "pass" : "password", + "name" : "live" + }, + "one" : { + "two" : { + "three" : "multi" + } + } +}, +"staging" : { + "hostname" : "staging", + "db" : { + "name" : "dbstaging" + }, + "debug" : "false" +}, +"debug" : { + "hostname" : "debug", + "debug" : "true", + "values" : { + "changed" : "yes" + }, + "db" : { + "name" : "dbdebug" + }, + "special" : { + "no" : "no", + "null" : "null", + "false" : "false" + } +}, +"other_staging" : { + "only_in" : "otherStaging", + "db" : { + "pass" : "anotherpwd" + } +} +} \ No newline at end of file From c2dc8a7a7eaad2c06d07867d7c2320e237620e72 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Fri, 30 Mar 2012 18:51:14 +0200 Subject: [PATCH 2/2] Yaml reader/writer for Zend\Config --- src/Reader/Yaml.php | 160 ++++++++++++++++++ src/Writer/Yaml.php | 95 +++++++++++ test/Reader/TestAssets/Yaml/include-base.yaml | 1 + .../TestAssets/Yaml/include-target.yaml | 1 + test/Reader/YamlTest.php | 104 ++++++++++++ test/Writer/YamlTest.php | 87 ++++++++++ test/Writer/files/allsections.yaml | 35 ++++ 7 files changed, 483 insertions(+) create mode 100644 src/Reader/Yaml.php create mode 100644 src/Writer/Yaml.php create mode 100644 test/Reader/TestAssets/Yaml/include-base.yaml create mode 100644 test/Reader/TestAssets/Yaml/include-target.yaml create mode 100644 test/Reader/YamlTest.php create mode 100644 test/Writer/YamlTest.php create mode 100644 test/Writer/files/allsections.yaml diff --git a/src/Reader/Yaml.php b/src/Reader/Yaml.php new file mode 100644 index 0000000..d8aa081 --- /dev/null +++ b/src/Reader/Yaml.php @@ -0,0 +1,160 @@ +setYamlDecoder($yamlDecoder); + } else { + if (function_exists('yaml_parse')) { + $this->setYamlDecoder('yaml_parse'); + } + } + } + /** + * Get callback for decoding YAML + * + * @return callable + */ + public function getYamlDecoder() + { + return $this->yamlDecoder; + } + /** + * Set callback for decoding YAML + * + * @param callable $yamlDecoder the decoder to set + * @return Yaml + */ + public function setYamlDecoder($yamlDecoder) + { + if (!is_callable($yamlDecoder)) { + throw new Exception\InvalidArgumentException('Invalid parameter to setYamlDecoder() - must be callable'); + } + $this->yamlDecoder = $yamlDecoder; + return $this; + } + /** + * fromFile(): defined by Reader interface. + * + * @see Reader::fromFile() + * @param string $filename + * @return array + */ + public function fromFile($filename) + { + if (!file_exists($filename)) { + throw new Exception\RuntimeException("The file $filename doesn't exists."); + } + if (null === $this->getYamlDecoder()) { + throw new Exception\RuntimeException("You didn't specify a Yaml callback decoder"); + } + + $this->directory = dirname($filename); + + $config = call_user_func($this->getYamlDecoder(), file_get_contents($filename)); + if (null === $config) { + throw new Exception\RuntimeException("Error parsing YAML data"); + } + + return $this->process($config); + } + + /** + * fromString(): defined by Reader interface. + * + * @see Reader::fromString() + * @param string $string + * @return array + */ + public function fromString($string) + { + if (null === $this->getYamlDecoder()) { + throw new Exception\RuntimeException("You didn't specify a Yaml callback decoder"); + } + if (empty($string)) { + return array(); + } + + $this->directory = null; + + $config = call_user_func($this->getYamlDecoder(), $string); + if (null === $config) { + throw new Exception\RuntimeException("Error parsing YAML data"); + } + + return $this->process($config); + } + /** + * Process the array for @include + * + * @param array $data + * @return array + */ + protected function process(array $data) { + foreach ($data as $key => $value) { + if (is_array($value)) { + $data[$key] = $this->process($value); + } + if (trim($key)==='@include') { + if ($this->directory === null) { + throw new Exception\RuntimeException('Cannot process @include statement for a json string'); + } + $reader = clone $this; + unset($data[$key]); + $data = array_replace_recursive($data, $reader->fromFile($this->directory . '/' . $value)); + } + } + return $data; + } +} \ No newline at end of file diff --git a/src/Writer/Yaml.php b/src/Writer/Yaml.php new file mode 100644 index 0000000..6a5c1a0 --- /dev/null +++ b/src/Writer/Yaml.php @@ -0,0 +1,95 @@ +setYamlEncoder($yamlEncoder); + } else { + if (function_exists('yaml_parse')) { + $this->setYamlEncoder('yaml_parse'); + } + } + } + /** + * Get callback for decoding YAML + * + * @return callable + */ + public function getYamlEncoder() + { + return $this->yamlEncoder; + } + /** + * Set callback for decoding YAML + * + * @param callable $yamlEncoder the decoder to set + * @return Yaml + */ + public function setYamlEncoder($yamlEncoder) + { + if (!is_callable($yamlEncoder)) { + throw new Exception\InvalidArgumentException('Invalid parameter to setYamlEncoder() - must be callable'); + } + $this->yamlEncoder = $yamlEncoder; + return $this; + } + /** + * processConfig(): defined by AbstractWriter. + * + * @param array $config + * @return string + */ + public function processConfig(array $config) + { + if (null === $this->getYamlEncoder()) { + throw new Exception\RuntimeException("You didn't specify a Yaml callback encoder"); + } + + $config = call_user_func($this->getYamlEncoder(), $config); + if (null === $config) { + throw new Exception\RuntimeException("Error generating YAML data"); + } + + return $config; + } +} diff --git a/test/Reader/TestAssets/Yaml/include-base.yaml b/test/Reader/TestAssets/Yaml/include-base.yaml new file mode 100644 index 0000000..4f891f2 --- /dev/null +++ b/test/Reader/TestAssets/Yaml/include-base.yaml @@ -0,0 +1 @@ +@include: include-target.yaml \ No newline at end of file diff --git a/test/Reader/TestAssets/Yaml/include-target.yaml b/test/Reader/TestAssets/Yaml/include-target.yaml new file mode 100644 index 0000000..d867783 --- /dev/null +++ b/test/Reader/TestAssets/Yaml/include-target.yaml @@ -0,0 +1 @@ +foo: foo \ No newline at end of file diff --git a/test/Reader/YamlTest.php b/test/Reader/YamlTest.php new file mode 100644 index 0000000..ea8087f --- /dev/null +++ b/test/Reader/YamlTest.php @@ -0,0 +1,104 @@ +markTestSkipped('Yaml test for Zend\Config skipped'); + } + + if (constant('TESTS_ZEND_CONFIG_YAML_LIB_INCLUDE')) { + require_once constant('TESTS_ZEND_CONFIG_YAML_LIB_INCLUDE'); + } + + $yamlReader = explode('::', constant('TESTS_ZEND_CONFIG_READER_YAML_CALLBACK')); + if (isset($yamlReader[1])) { + $this->reader = new YamlReader(array($yamlReader[0], $yamlReader[1])); + } else { + $this->reader = new YamlReader(array($yamlReader[0])); + } + } + + /** + * getTestAssetPath(): defined by AbstractReaderTestCase. + * + * @see AbstractReaderTestCase::getTestAssetPath() + * @return string + */ + protected function getTestAssetPath($name) + { + return __DIR__ . '/TestAssets/Yaml/' . $name . '.yaml'; + } + + public function testInvalidIniFile() + { + $this->setExpectedException('Zend\Config\Exception\RuntimeException'); + $arrayIni = $this->reader->fromFile($this->getTestAssetPath('invalid')); + } + + public function testFromString() + { + $yaml = <<reader->fromString($yaml); + $this->assertEquals($arrayYaml['test'], 'foo'); + $this->assertEquals($arrayYaml['bar'][0], 'baz'); + $this->assertEquals($arrayYaml['bar'][1], 'foo'); + } + + public function testFromStringWithSection() + { + $yaml = <<reader->fromString($yaml); + $this->assertEquals($arrayYaml['all']['test'], 'foo'); + $this->assertEquals($arrayYaml['all']['bar'][0], 'baz'); + $this->assertEquals($arrayYaml['all']['bar'][1], 'foo'); + } +} diff --git a/test/Writer/YamlTest.php b/test/Writer/YamlTest.php new file mode 100644 index 0000000..73e9a40 --- /dev/null +++ b/test/Writer/YamlTest.php @@ -0,0 +1,87 @@ +markTestSkipped('Yaml test for Zend\Config skipped'); + } + + if (constant('TESTS_ZEND_CONFIG_YAML_LIB_INCLUDE')) { + require_once constant('TESTS_ZEND_CONFIG_YAML_LIB_INCLUDE'); + } + + $yamlReader = explode('::', constant('TESTS_ZEND_CONFIG_READER_YAML_CALLBACK')); + if (isset($yamlReader[1])) { + $this->reader = new YamlReader(array($yamlReader[0], $yamlReader[1])); + } else { + $this->reader = new YamlReader(array($yamlReader[0])); + } + + $yamlWriter = explode('::', constant('TESTS_ZEND_CONFIG_WRITER_YAML_CALLBACK')); + if (isset($yamlWriter[1])) { + $this->writer = new YamlWriter(array($yamlWriter[0], $yamlWriter[1])); + } else { + $this->writer = new YamlWriter(array($yamlWriter[0])); + } + } + + public function testNoSection() + { + $config = new Config(array('test' => 'foo', 'test2' => array('test3' => 'bar'))); + + $this->writer->toFile($this->getTestAssetFileName(), $config); + + $config = $this->reader->fromFile($this->getTestAssetFileName()); + + $this->assertEquals('foo', $config['test']); + $this->assertEquals('bar', $config['test2']['test3']); + } + + public function testWriteAndReadOriginalFile() + { + $config = $this->reader->fromFile(__DIR__ . '/files/allsections.yaml'); + + $this->writer->toFile($this->getTestAssetFileName(), $config); + + $config = $this->reader->fromFile($this->getTestAssetFileName()); + + $this->assertEquals('multi', $config['all']['one']['two']['three']); + + } +} diff --git a/test/Writer/files/allsections.yaml b/test/Writer/files/allsections.yaml new file mode 100644 index 0000000..1dafe98 --- /dev/null +++ b/test/Writer/files/allsections.yaml @@ -0,0 +1,35 @@ +all: +# this is a comment + hostname: all + name: thisname + db: + host: 127.0.0.1 + user: username + pass: password + name: live + one: + two: + three: multi + +staging: + hostname: staging + db: + name: dbstaging + debug: + _extends: all +debug: + hostname: debug + debug: 1 + values: + changed: 1 + db: + name: dbdebug + special: + no: + null: + false: + _extends: all +other_staging: + only_in: otherStaging + db: + pass: anotherpwd