Procházet zdrojové kódy

[1.11] Promoted Zend_Config_Yaml/Json to trunk

- Updated AllTests files to reflect additions
- Updated documentation to reflect additions
- Fixed issue found when running full suite

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@23103 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew před 15 roky
rodič
revize
3b0b5a7e92
31 změnil soubory, kde provedl 2665 přidání a 18 odebrání
  1. 10 0
      documentation/manual/en/manual.xml.in
  2. 249 0
      documentation/manual/en/module_specs/Zend_Config_Json.xml
  3. 71 18
      documentation/manual/en/module_specs/Zend_Config_Writer.xml
  4. 281 0
      documentation/manual/en/module_specs/Zend_Config_Yaml.xml
  5. 240 0
      library/Zend/Config/Json.php
  6. 106 0
      library/Zend/Config/Writer/Json.php
  7. 144 0
      library/Zend/Config/Writer/Yaml.php
  8. 381 0
      library/Zend/Config/Yaml.php
  9. 4 0
      tests/Zend/Config/AllTests.php
  10. 286 0
      tests/Zend/Config/JsonTest.php
  11. 4 0
      tests/Zend/Config/Writer/AllTests.php
  12. 176 0
      tests/Zend/Config/Writer/JsonTest.php
  13. 165 0
      tests/Zend/Config/Writer/YamlTest.php
  14. 47 0
      tests/Zend/Config/Writer/files/allsections-pretty.json
  15. 1 0
      tests/Zend/Config/Writer/files/allsections.json
  16. 36 0
      tests/Zend/Config/Writer/files/allsections.yaml
  17. 315 0
      tests/Zend/Config/YamlTest.php
  18. 1 0
      tests/Zend/Config/_files/allsections.json
  19. 35 0
      tests/Zend/Config/_files/allsections.yaml
  20. 22 0
      tests/Zend/Config/_files/array.yaml
  21. 6 0
      tests/Zend/Config/_files/badindentation.yaml
  22. 29 0
      tests/Zend/Config/_files/booleans.yaml
  23. 1 0
      tests/Zend/Config/_files/circular.json
  24. 9 0
      tests/Zend/Config/_files/circular.yaml
  25. 1 0
      tests/Zend/Config/_files/config.json
  26. 37 0
      tests/Zend/Config/_files/config.yaml
  27. 3 0
      tests/Zend/Config/_files/constants.yaml
  28. 1 0
      tests/Zend/Config/_files/invalid.json
  29. 2 0
      tests/Zend/Config/_files/invalid.yaml
  30. 1 0
      tests/Zend/Config/_files/multipleinheritance.json
  31. 1 0
      tests/Zend/Config/_files/nosections.json

+ 10 - 0
documentation/manual/en/manual.xml.in

@@ -531,11 +531,21 @@
                     <xi:include href="../en/module_specs/Zend_Config_Ini.xml" />
                 </xi:fallback>
             </xi:include>
+            <xi:include href="module_specs/Zend_Config_Json.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Config_Json.xml" />
+                </xi:fallback>
+            </xi:include>
             <xi:include href="module_specs/Zend_Config_Xml.xml">
                 <xi:fallback>
                     <xi:include href="../en/module_specs/Zend_Config_Xml.xml" />
                 </xi:fallback>
             </xi:include>
+            <xi:include href="module_specs/Zend_Config_Yaml.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Config_Yaml.xml" />
+                </xi:fallback>
+            </xi:include>
         </chapter>
 
         <chapter id="zend.config.writer">

+ 249 - 0
documentation/manual/en/module_specs/Zend_Config_Json.xml

@@ -0,0 +1,249 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.config.adapters.json">
+    <title>Zend_Config_Json</title>
+
+    <sect2 id="zend.config.adapters.json.intro">
+        <title>Overview</title>
+
+        <para>
+            <ulink url="http://www.json.org/">JSON</ulink> is an acronym for "JavaScript Object
+            Notation"; while compatible with JavaScript, it is also intended as a general-purpose,
+            cross-language data interchange format.  <classname>Zend_Config_Json</classname> is a
+            lightweight <classname>Zend_Config</classname> extension using <acronym>JSON</acronym>
+            as its serialization format.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.json.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            The following is a <acronym>JSON</acronym> version of a standard application configuration.
+        </para>
+
+        <programlisting language="json"><![CDATA[
+{
+    "production":{
+        "phpSettings":{
+            "display_startup_errors": false,
+            "display_errors": false
+        },
+        "includePaths":{
+            "library": "APPLICATION_PATH/../library"
+        },
+        "bootstrap":{
+            "path": "APPLICATION_PATH/Bootstrap.php",
+            "class": "Bootstrap"
+        },
+        "appnamespace": "Application",,
+        "resources":{
+            "frontController":{
+                "controllerDirectory": "APPLICATION_PATH/controllers",
+                "moduleDirectory": "APPLICATION_PATH/modules",
+                "params":{
+                    "displayExceptions": false
+                }
+            },
+            "modules":[],
+            "db":{
+                "adapter": "pdo_sqlite",
+                "params":{
+                    "dbname": "APPLICATION_PATH/../data/db/application.db"
+                }
+            },
+            "layout":{
+                "layoutPath": "APPLICATION_PATH/layouts/scripts/"
+            }
+        }
+    },
+    "staging":{
+        "_extends": "production"
+    },
+    "testing":{
+        "_extends": "production",
+        "phpSettings":{
+            "display_startup_errors": true,
+            "display_errors": true
+        },
+    },
+    "development":{
+        "_extends": "production",
+        "phpSettings":{
+            "display_startup_errors": true,
+            "display_errors": true
+        },
+        "resources":{
+            "frontController":{
+                "params":{
+                    "displayExceptions": true
+                }
+            }
+        }
+    }
+}
+]]></programlisting>
+
+        <para>
+            To utilize it, you simply instantiate <classname>Zend_Config_Json</classname>, pointing
+            it to the location of this file and indicating the section of the file to load. By
+            default, constant names found in values will be substituted with their appropriate
+            values.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$config = new Zend_Config_Json(
+    APPLICATION_PATH . '/configs/application.json',
+    APPLICATION_ENV
+);
+]]></programlisting>
+
+        <para>
+            Once instantiated, you use it as you would any other configuration object.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$db = Zend_Db::factory($config->resources->db);
+]]></programlisting>
+
+        <warning>
+            <title>Use Constants With Care</title>
+
+            <para>
+                <acronym>JSON</acronym> has a strict structure with regards to data types. As such,
+                you need to ensure that your constants are use correctly. For constants that have
+                string values, put your constant values in double quotes (""). For non-string
+                values, you can omit the quotes -- but be absolutely certain that they are not
+                returning strings, as otherwise you will encounter parser errors with your
+                configuration file. When in doubt, enclose the contant in double quotes.
+            </para>
+        </warning>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.json.options">
+        <title>Configuration Options</title>
+
+        <para>
+            The following options may be passed as keys to the third, <varname>$options</varname>
+            argument of the constructor.
+        </para>
+
+        <variablelist>
+            <title>Zend_Config_Json Options</title>
+
+            <varlistentry>
+                <term>allow_modifications/allowModifications</term>
+
+                <listitem>
+                    <para>
+                        The default behavior of <classname>Zend_Config</classname> is to mark the
+                        object as immutable once loaded. Passing this flag with a boolean
+                        <constant>true</constant> will enable modifications to the object.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>skip_extends/skipExtends</term>
+
+                <listitem>
+                    <para>
+                        By default, any time a section extends another,
+                        <classname>Zend_Config</classname> will merge the section with the section
+                        it extends. Speciying a boolean <constant>true</constant> value to this
+                        option will disable this feature, giving you only the configuration defined
+                        explicitly in that section.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>ignore_constants</term>
+
+                <listitem>
+                    <para>
+                        By default, <classname>Zend_Config_Json</classname> will replace constant
+                        names found in values with the defined constant value. You map pass a
+                        boolean <constant>true</constant> to this option to disable this
+                        functionality.
+                    </para>
+
+                    <para>
+                        Please note that ignoring constants can potentially lead to parse errors,
+                        particularly if you are using constants for integer, float, or boolean
+                        values. The safest practice is to enclose constants within quotes.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.json.methods">
+        <title>Available Methods</title>
+
+        <variablelist>
+            <varlistentry id="zend.config.adapters.json.methods.constructor">
+                <term>
+                    <methodsynopsis>
+                        <methodname>__construct</methodname>
+                        <methodparam>
+                            <funcparams>$json, $section = null, $options = false</funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        Constructor. <varname>$json</varname> should be either a valid
+                        <acronym>JSON</acronym> string, or refer to a valid filesystem location
+                        containing a <acronym>JSON</acronym> configuration file.
+                        <varname>$section</varname>, if specified, indicates a specific section of
+                        the configuration file to use.  <varname>$options</varname> is discussed in
+                        the <link linkend="zend.config.adapters.json.options">options
+                            section</link>.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry id="zend.config.adapters.json.methods.set-ignore-constants">
+                <term>
+                    <methodsynopsis>
+                        <methodname>setIgnoreConstants</methodname>
+                        <methodparam>
+                            <funcparams>$flag</funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        This <emphasis>static</emphasis> function may be used to globally override
+                        the default settings for how constants found in <acronym>JSON</acronym>
+                        strings are handled. By default, constant names are replaced with the
+                        appropriate constant values; passing a boolean <constant>true</constant>
+                        value to this method will override that behavior. (You can override it
+                        per-instance via the <varname>ignore_constants</varname> option as well.)
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry id="zend.config.adapters.json.methods.ignore-constants">
+                <term>
+                    <methodsynopsis>
+                        <methodname>ignoreConstants</methodname>
+                        <methodparam>
+                            <funcparams></funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        This <emphasis>static</emphasis> method gives you the current setting for
+                        the <varname>ignore_constants</varname> flag.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+</sect1>

+ 71 - 18
documentation/manual/en/module_specs/Zend_Config_Writer.xml

@@ -7,7 +7,7 @@
         <classname>Zend_Config_Writer</classname> gives you the ability to write config
         files out of <classname>Zend_Config</classname> objects. It works with an
         adapter-less system and thus is very easy to use. By default
-        <classname>Zend_Config_Writer</classname> ships with three adapters, which are all
+        <classname>Zend_Config_Writer</classname> ships with four adapters, which are all
         file-based. You instantiate a writer with specific options, which
         can be <emphasis>filename</emphasis> and <emphasis>config</emphasis>. Then
         you call the <methodname>write()</methodname> method of the writer and the config
@@ -32,24 +32,23 @@
 
         <listitem>
             <para>
+                <classname>Zend_Config_Writer_Json</classname>
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
                 <classname>Zend_Config_Writer_Xml</classname>
             </para>
         </listitem>
-    </itemizedlist>
 
-    <para>
-        The <acronym>INI</acronym> writer has two modes for rendering with regard to sections.
-        By default the top-level configuration is always written into section names.
-        By calling <command>$writer->setRenderWithoutSections();</command> all options are written
-        into the global namespace of the <acronym>INI</acronym> file and no sections are applied.
-    </para>
+        <listitem>
+            <para>
+                <classname>Zend_Config_Writer_Yaml</classname>
+            </para>
+        </listitem>
+    </itemizedlist>
 
-    <para>
-        As an addition <classname>Zend_Config_Writer_Ini</classname> has an additional
-        option parameter <emphasis>nestSeparator</emphasis>, which defines with which
-        character the single nodes are separated. The default is a single dot,
-        like it is accepted by <classname>Zend_Config_Ini</classname> by default.
-    </para>
 
     <para>
         When modifying or creating a <classname>Zend_Config</classname> object, there are
@@ -144,11 +143,65 @@ $writer->write();
     </note>
 
     <para>
-        For all the File-Based writers (<acronym>INI</acronym>, <acronym>XML</acronym> and
-        <acronym>PHP</acronym> Array) internally the <methodname>render()</methodname> is used to
-        build the configuration string. This method can be used from the outside also if you need
-        to access the string-representation of the configuration data.
+        For all the File-Based writers (<acronym>INI</acronym>, <acronym>JSON</acronym>,
+        <acronym>XML</acronym>, <acronym>YAML</acronym>, and <acronym>PHP</acronym> Array)
+        internally the <methodname>render()</methodname> is used to build the configuration string.
+        This method can be used independently to access the string-representation of the
+        configuration data.
     </para>
+
+    <sect2 id="zend.config.writer.introduction.ini-notes">
+        <title>Notes specific to the INI writer</title>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    The <acronym>INI</acronym> writer has two modes for rendering with regard to
+                    sections.  By default the top-level configuration is always written into section
+                    names.  By calling <command>$writer->setRenderWithoutSections();</command> all
+                    options are written into the global namespace of the <acronym>INI</acronym> file
+                    and no sections are applied.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <classname>Zend_Config_Writer_Ini</classname> has an additional option parameter
+                    <emphasis>nestSeparator</emphasis>, which defines with which character the
+                    single nodes are separated. The default is a single dot, which is accepted by
+                    <classname>Zend_Config_Ini</classname> by default.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+
+    <sect2 id="zend.config.writer.introduction.yaml-notes">
+        <title>Notes specific to the YAML writer</title>
+
+        <para>
+            The <acronym>YAML</acronym> writer lets you optionally specify an alternate
+            <acronym>YAML</acronym> encoder to use. By default, one is shipped with the framework
+            that is suitable for most configuration tasks. If you find it insufficient, or wish to
+            use more advanced YAML, you may provide an alternate encoder callback.
+        </para>
+
+        <para>
+            The method for doing so is to use the
+            <methodname>Zend_Config_Writer_Yaml::setYamlEncoder()</methodname> method, passing it a
+            valid callback.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// Use the Symfony Yaml Component:
+$writer = new Zend_Config_Writer_Yaml($filename);
+$writer->setYamlEncoder(array('sfYaml', 'dump'));
+]]></programlisting>
+
+        <para>
+            The above uses the Symfony Components' <classname>sfYaml</classname> component in order
+            to encode the configuration to <acronym>YAML</acronym>.
+        </para>
+    </sect2>
 </sect1>
 <!--
 vim:se ts=4 sw=4 et:

+ 281 - 0
documentation/manual/en/module_specs/Zend_Config_Yaml.xml

@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.config.adapters.yaml">
+    <title>Zend_Config_Yaml</title>
+
+    <sect2 id="zend.config.adapters.yaml.intro">
+        <title>Overview</title>
+
+        <para>
+            <ulink url="http://www.yaml.org/">YAML</ulink> is a recursive acronym meaning "YAML
+            Ain't Markup Language", and is intended as a "human friendly data serialization
+            standard for all programming languages." It is often used for application configuration.
+        </para>
+
+        <para>
+            <classname>Zend_Config_Yaml</classname> is a lightweight
+            <classname>Zend_Config</classname> extension. It includes a parser capable of
+            recognizing most common YAML syntax used for purposes of configuration, and allows
+            specifying other parsers should you want more complex syntax (e.g., ext/syck, spyc,
+            sfYaml, etc.).
+        </para>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.yaml.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            The following is a YAML version of a standard application configuration.
+        </para>
+
+        <programlisting language="yaml"><![CDATA[
+production:
+  phpSettings:
+    display_startup_errors: false
+    display_errors: false
+  includePaths:
+    library: APPLICATION_PATH/../library
+  bootstrap:
+    path: APPLICATION_PATH/Bootstrap.php
+    class: "Bootstrap"
+  appnamespace: "Application"
+  resources:
+    frontController:
+      controllerDirectory: APPLICATION_PATH/controllers
+      moduleDirectory: APPLICATION_PATH/modules
+      params:
+        displayExceptions: false
+    modules:
+    db:
+      adapter: "pdo_sqlite"
+      params:
+        dbname: APPLICATION_PATH/../data/db/application.db
+    layout:
+      layoutPath: APPLICATION_PATH/layouts/scripts/
+
+staging: 
+  _extends: production
+
+testing: 
+  _extends: production
+  phpSettings:
+    display_startup_errors: true
+    display_errors: true
+
+development:
+  _extends: production
+  phpSettings:
+    display_startup_errors: true
+    display_errors: true
+  resources:
+    frontController:
+      params:
+        displayExceptions: true
+
+]]></programlisting>
+
+        <para>
+            To utilize it, you simply instantiate <classname>Zend_Config_Yaml</classname>, pointing
+            it to the location of this file and indicating the section of the file to load. By
+            default, constant names found in values will be substituted with their appropriate
+            values.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$config = new Zend_Config_Yaml(
+    APPLICATION_PATH . '/configs/application.yaml',
+    APPLICATION_ENV
+);
+]]></programlisting>
+
+        <para>
+            Once instantiated, you use it as you would any other configuration object.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$db = Zend_Db::factory($config->resources->db);
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.yaml.options">
+        <title>Configuration Options</title>
+
+        <para>
+            The following options may be passed as keys to the third, <varname>$options</varname>
+            argument of the constructor.
+        </para>
+
+        <variablelist>
+            <title>Zend_Config_Yaml Options</title>
+
+            <varlistentry>
+                <term>allow_modifications</term>
+
+                <listitem>
+                    <para>
+                        The default behavior of <classname>Zend_Config</classname> is to mark the
+                        object as immutable once loaded. Passing this flag with a boolean
+                        <constant>true</constant> will enable modifications to the object.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>skip_extends</term>
+
+                <listitem>
+                    <para>
+                        By default, any time a section extends another,
+                        <classname>Zend_Config</classname> will merge the section with the section
+                        it extends. Speciying a boolean <constant>true</constant> value to this
+                        option will disable this feature, giving you only the configuration defined
+                        explicitly in that section.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>ignore_constants</term>
+
+                <listitem>
+                    <para>
+                        By default, <classname>Zend_Config_Yaml</classname> will replace constant
+                        names found in values with the defined constant value. You map pass a
+                        boolean <constant>true</constant> to this option to disable this
+                        functionality.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>yaml_decoder</term>
+
+                <listitem>
+                    <para>
+                        By default, <classname>Zend_Config_Yaml</classname> uses a built in decoder,
+                        <methodname>Zend_Config_Yaml::decode()</methodname>, to parse and process
+                        YAML files. You may specify an alternate callback to use in place of the
+                        built-in one using this option.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.yaml.methods">
+        <title>Available Methods</title>
+
+        <variablelist>
+            <varlistentry id="zend.config.adapters.yaml.methods.constructor">
+                <term>
+                    <methodsynopsis>
+                        <methodname>__construct</methodname>
+                        <methodparam>
+                            <funcparams>$yaml, $section = null, $options = false</funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        Constructor. <varname>$yaml</varname> should refer to a valid filesystem
+                        location containing a YAML configuration file. <varname>$section</varname>,
+                        if specified, indicates a specific section of the configuration file to use.
+                        <varname>$options</varname> is discussed in the <link
+                            linkend="zend.config.adapters.yaml.options">options section</link>.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry id="zend.config.adapters.yaml.methods.decode">
+                <term>
+                    <methodsynopsis>
+                        <methodname>decode</methodname>
+                        <methodparam>
+                            <funcparams>$yaml</funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        Parses a YAML string into a PHP array.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry id="zend.config.adapters.yaml.methods.set-ignore-constants">
+                <term>
+                    <methodsynopsis>
+                        <methodname>setIgnoreConstants</methodname>
+                        <methodparam>
+                            <funcparams>$flag</funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        This <emphasis>static</emphasis> function may be used to globally override
+                        the default settings for how constants found in YAML strings are handled. By
+                        default, constant names are replaced with the appropriate constant values;
+                        passing a boolean <constant>true</constant> value to this method will
+                        override that behavior. (You can override it per-instance via the
+                        <varname>ignore_constants</varname> option as well.)
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry id="zend.config.adapters.yaml.methods.ignore-constants">
+                <term>
+                    <methodsynopsis>
+                        <methodname>ignoreConstants</methodname>
+                        <methodparam>
+                            <funcparams></funcparams>
+                        </methodparam>
+                    </methodsynopsis>
+                </term>
+
+                <listitem>
+                    <para>
+                        This <emphasis>static</emphasis> method gives you the current setting for
+                        the <varname>ignore_constants</varname> flag.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+
+    <sect2 id="zend.config.adapters.yaml.examples">
+        <title>Examples</title>
+
+        <example id="zend.config.adapters.yaml.examples.sf-yaml">
+            <title>Using Zend_Config_Yaml with sfYaml</title>
+
+            <para>
+                As noted in the <link linkend="zend.config.adapters.yaml.options">options
+                    section</link>, <classname>Zend_Config_Yaml</classname> allows you to specify an
+                alternate YAML parser at instantiation.
+            </para>
+
+            <para>
+                <ulink url="http://components.symfony-project.org/yaml/">sfYaml</ulink> is a <ulink
+                    url="http://components.symfony-project.org/">Symfony component</ulink> that
+                implements a complete YAML parser in PHP, and includes a number of additional
+                features including the ability to parse PHP expressions embedded in the YAML. In
+                this example, we use the <methodname>sfYaml::load()</methodname> method as our YAML
+                decoder callback. <emphasis>(Note: this assumes that the
+                    <classname>sfYaml</classname> class is either already loaded or available via
+                    autoloading.)</emphasis>
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$config = new Zend_Config_Yaml(
+    APPLICATION_PATH . '/configs/application.yaml',
+    APPLICATION_ENV,
+    array('yaml_decoder' => array('sfYaml', 'load'))
+);
+]]></programlisting>
+        </example>
+    </sect2>
+</sect1>

+ 240 - 0
library/Zend/Config/Json.php

@@ -0,0 +1,240 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Config
+ * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Xml.php 19059 2009-11-19 20:05:27Z jan $
+ */
+
+/**
+ * @see Zend_Config
+ */
+require_once 'Zend/Config.php';
+
+/**
+ * @see Zend_Json
+ */
+require_once 'Zend/Json.php';
+
+/**
+ * XML Adapter for Zend_Config
+ *
+ * @category  Zend
+ * @package   Zend_Config
+ * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_Json extends Zend_Config
+{
+    /**
+     * Name of object key indicating section current section extends
+     */
+    const EXTENDS_NAME = "_extends";
+
+    /**
+     * Whether or not to ignore constants in the JSON string
+     *
+     * Note: if you do not have constant names in quotations in your JSON 
+     * string, they may lead to syntax errors when parsing.
+     * 
+     * @var bool
+     */
+    protected $_ignoreConstants = false;
+
+    /**
+     * Whether to skip extends or not
+     *
+     * @var boolean
+     */
+    protected $_skipExtends = false;
+
+    /**
+     * Loads the section $section from the config file encoded as JSON
+     *
+     * Sections are defined as properties of the main object
+     *
+     * In order to extend another section, a section defines the "_extends"
+     * property having a value of the section name from which the extending
+     * section inherits values.
+     *
+     * Note that the keys in $section will override any keys of the same
+     * name in the sections that have been included via "_extends".
+     *
+     * @param  string  $json     JSON file or string to process
+     * @param  mixed   $section Section to process
+     * @param  boolean $options Whether modifiacations are allowed at runtime
+     * @throws Zend_Config_Exception When JSON text is not set or cannot be loaded
+     * @throws Zend_Config_Exception When section $sectionName cannot be found in $json
+     */
+    public function __construct($json, $section = null, $options = false)
+    {
+        if (empty($json)) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception('Filename is not set');
+        }
+
+        $allowModifications = false;
+        if (is_bool($options)) {
+            $allowModifications = $options;
+        } elseif (is_array($options)) {
+            foreach ($options as $key => $value) {
+                switch (strtolower($key)) {
+                    case 'allow_modifications':
+                    case 'allowmodifications':
+                        $allowModifications = (bool) $value;
+                        break;
+                    case 'skip_extends':
+                    case 'skipextends':
+                        $this->_skipExtends = (bool) $value;
+                        break;
+                    case 'ignore_constants':
+                    case 'ignoreconstants':
+                        $this->_ignoreConstants = (bool) $value;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        set_error_handler(array($this, '_loadFileErrorHandler')); // Warnings and errors are suppressed
+        if ($json[0] != '{') {
+            $json = file_get_contents($json);
+        }
+        restore_error_handler();
+
+        // Check if there was a error while loading file
+        if ($this->_loadFileErrorStr !== null) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception($this->_loadFileErrorStr);
+        }
+
+        // Replace constants
+        if (!$this->_ignoreConstants) {
+            $json = $this->_replaceConstants($json);
+        }
+
+        // Parse/decode
+        $config = Zend_Json::decode($json);
+        
+        if (null === $config) {
+            // decode failed
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception("Error parsing JSON data");
+        }
+
+        if ($section === null) {
+            $dataArray = array();
+            foreach ($config as $sectionName => $sectionData) {
+                $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
+            }
+
+            parent::__construct($dataArray, $allowModifications);
+        } elseif (is_array($section)) {
+            $dataArray = array();
+            foreach ($section as $sectionName) {
+                if (!isset($config[$sectionName])) {
+                    require_once 'Zend/Config/Exception.php';
+                    throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $sectionName));
+                }
+
+                $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
+            }
+
+            parent::__construct($dataArray, $allowModifications);
+        } else {
+            if (!isset($config[$section])) {
+                require_once 'Zend/Config/Exception.php';
+                throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+            }
+
+            $dataArray = $this->_processExtends($config, $section);
+            if (!is_array($dataArray)) {
+                // Section in the JSON data contains just one top level string
+                $dataArray = array($section => $dataArray);
+            }
+
+            parent::__construct($dataArray, $allowModifications);
+        }
+
+        $this->_loadedSection = $section;
+    }
+
+    /**
+     * Helper function to process each element in the section and handle
+     * the "_extends" inheritance attribute.
+     *
+     * @param  array            $data Data array to process
+     * @param  string           $section Section to process
+     * @param  array            $config  Configuration which was parsed yet
+     * @throws Zend_Config_Exception When $section cannot be found
+     * @return array
+     */
+    protected function _processExtends(array $data, $section, array $config = array())
+    {
+        if (!isset($data[$section])) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+        }
+
+        $thisSection  = $data[$section];
+
+        if (is_array($thisSection) && isset($thisSection[self::EXTENDS_NAME])) {
+            if (is_array($thisSection[self::EXTENDS_NAME])) {
+                require_once 'Zend/Config/Exception.php';
+                throw new Zend_Config_Exception('Invalid extends clause: must be a string; array received');
+            }
+            $this->_assertValidExtend($section, $thisSection[self::EXTENDS_NAME]);
+
+            if (!$this->_skipExtends) {
+                $config = $this->_processExtends($data, $thisSection[self::EXTENDS_NAME], $config);
+            }
+            unset($thisSection[self::EXTENDS_NAME]);
+        }
+
+        $config = $this->_arrayMergeRecursive($config, $thisSection);
+
+        return $config;
+    }
+
+    /**
+     * Replace any constants referenced in a string with their values
+     * 
+     * @param  string $value 
+     * @return string
+     */
+    protected function _replaceConstants($value)
+    {
+        foreach ($this->_getConstants() as $constant) {
+            if (strstr($value, $constant)) {
+                $value = str_replace($constant, constant($constant), $value);
+            }
+        }
+        return $value;
+    }
+
+    /**
+     * Get (reverse) sorted list of defined constant names
+     * 
+     * @return array
+     */
+    protected function _getConstants()
+    {
+        $constants = array_keys(get_defined_constants());
+        rsort($constants, SORT_STRING);
+        return $constants;
+    }
+}

+ 106 - 0
library/Zend/Config/Writer/Json.php

@@ -0,0 +1,106 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Config
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Xml.php 18862 2009-11-05 18:25:20Z beberlei $
+ */
+
+/**
+ * @see Zend_Config_Writer
+ */
+require_once 'Zend/Config/Writer/FileAbstract.php';
+
+/**
+ * @see Zend_Config_Json
+ */
+require_once 'Zend/Config/Json.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Config
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_Writer_Json extends Zend_Config_Writer_FileAbstract
+{
+    /**
+     * If we need to pretty-print JSON data
+     * 
+     * @var boolean
+     */
+    protected $_prettyPrint = false;
+    
+    /**
+     * Get prettyPrint flag
+     * 
+     * @return the prettyPrint flag
+     */
+    public function prettyPrint() 
+    {
+        return $this->_prettyPrint;
+    }
+
+    /**
+     * Set prettyPrint flag
+     * 
+     * @param  bool $prettyPrint PrettyPrint flag
+     * @return Zend_Config_Writer_Json
+     */
+    public function setPrettyPrint($flag) 
+    {
+        $this->_prettyPrint = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Render a Zend_Config into a JSON config string.
+     *
+     * @since 1.10
+     * @return string
+     */
+    public function render()
+    {
+        $data        = $this->_config->toArray();
+        $sectionName = $this->_config->getSectionName();
+        $extends     = $this->_config->getExtends();
+
+        if (is_string($sectionName)) {
+            $data = array($sectionName => $data);
+        }
+        
+        foreach ($extends as $section => $parentSection) {
+            $data[$section][Zend_Config_Json::EXTENDS_NAME] = $parentSection;
+        }
+
+        // Ensure that each "extends" section actually exists
+        foreach ($data as $section => $sectionData) {
+            if (is_array($sectionData) && isset($sectionData[Zend_Config_Json::EXTENDS_NAME])) {
+                $sectionExtends = $sectionData[Zend_Config_Json::EXTENDS_NAME];
+                if (!isset($data[$sectionExtends])) {
+                    // Remove "extends" declaration if section does not exist
+                    unset($data[$section][Zend_Config_Json::EXTENDS_NAME]);
+                }
+            }
+        }
+
+        $out = Zend_Json::encode($data);
+        if ($this->prettyPrint()) {
+             $out = Zend_Json::prettyPrint($out);   
+        }
+        return $out;
+    }
+}

+ 144 - 0
library/Zend/Config/Writer/Yaml.php

@@ -0,0 +1,144 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Config
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Xml.php 18862 2009-11-05 18:25:20Z beberlei $
+ */
+
+/**
+ * @see Zend_Config_Writer
+ */
+require_once 'Zend/Config/Writer/FileAbstract.php';
+
+/**
+ * @see Zend_Config_Json
+ */
+require_once 'Zend/Config/Yaml.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Config
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_Writer_Yaml extends Zend_Config_Writer_FileAbstract
+{
+    /**
+     * What to call when we need to decode some YAML?
+     * 
+     * @var callable
+     */
+    protected $_yamlEncoder = array('Zend_Config_Writer_Yaml', 'encode'); 
+    
+    /**
+     * Get callback for decoding YAML
+     * 
+	 * @return callable
+	 */
+	public function getYamlEncoder() 
+	{
+		return $this->_yamlEncoder;
+	}
+
+	/**
+	 * Set callback for decoding YAML
+	 * 
+	 * @param  $yamlEncoder the decoder to set
+	 * @return Zend_Config_Yaml
+	 */
+	public function setYamlEncoder($yamlEncoder) 
+	{
+	    if (!is_callable($yamlEncoder)) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception('Invalid parameter to setYamlEncoder - must be callable');
+	    }
+	    
+		$this->_yamlEncoder = $yamlEncoder;
+		return $this;
+	}
+    
+    /**
+     * Render a Zend_Config into a YAML config string.
+     *
+     * @since 1.10
+     * @return string
+     */
+	public function render()
+    {
+        $data        = $this->_config->toArray();
+        $sectionName = $this->_config->getSectionName();
+        $extends     = $this->_config->getExtends();
+
+        if (is_string($sectionName)) {
+            $data = array($sectionName => $data);
+        }
+
+        foreach ($extends as $section => $parentSection) {
+            $data[$section][Zend_Config_Yaml::EXTENDS_NAME] = $parentSection;
+        }
+
+        // Ensure that each "extends" section actually exists
+        foreach ($data as $section => $sectionData) {
+            if (is_array($sectionData) && isset($sectionData[Zend_Config_Yaml::EXTENDS_NAME])) {
+                $sectionExtends = $sectionData[Zend_Config_Yaml::EXTENDS_NAME];
+                if (!isset($data[$sectionExtends])) {
+                    // Remove "extends" declaration if section does not exist
+                    unset($data[$section][Zend_Config_Yaml::EXTENDS_NAME]);
+                }
+            }
+        }
+        
+        return call_user_func($this->getYamlEncoder(), $data);
+    }
+
+    /**
+     * Very dumb YAML encoder
+     * 
+     * Until we have Zend_Yaml...
+     * 
+     * @param array $data YAML data
+     * @return string
+     */
+    public static function encode($data)
+    {
+        return self::_encodeYaml(0, $data);
+    }
+    
+    /**
+     * Service function for encoding YAML
+     * 
+     * @param int $indent Current indent level
+     * @param array $data Data to encode
+     * @return string
+     */
+    protected static function _encodeYaml($indent, $data) 
+    {
+        reset($data);
+        $result = "";
+        $numeric = is_numeric(key($data));
+
+        foreach($data as $key => $value) {
+            if(is_array($value)) {
+                $encoded = "\n".self::_encodeYaml($indent+1, $value);
+            } else {
+                $encoded = (string)$value."\n";
+            }
+            $result .= str_repeat("  ", $indent).($numeric?"- ":"$key: ").$encoded;
+        }
+        return $result;
+    }
+}

+ 381 - 0
library/Zend/Config/Yaml.php

@@ -0,0 +1,381 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Config
+ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @see Zend_Config
+ */
+require_once 'Zend/Config.php';
+
+/**
+ * XML Adapter for Zend_Config
+ *
+ * @category  Zend
+ * @package   Zend_Config
+ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_Yaml extends Zend_Config
+{
+    /**
+     * Attribute name that indicates what section a config extends from
+     */
+    const EXTENDS_NAME = "_extends";
+
+    /**
+     * Whether to skip extends or not
+     *
+     * @var boolean
+     */
+    protected $_skipExtends = false;
+    
+    /**
+     * What to call when we need to decode some YAML?
+     * 
+     * @var callable
+     */
+    protected $_yamlDecoder = array(__CLASS__, 'decode'); 
+
+    /**
+     * Whether or not to ignore constants in parsed YAML
+     * @var bool
+     */
+    protected static $_ignoreConstants = false;
+
+    /**
+     * Indicate whether parser should ignore constants or not
+     * 
+     * @param  bool $flag 
+     * @return void
+     */
+    public static function setIgnoreConstants($flag)
+    {
+        self::$_ignoreConstants = (bool) $flag;
+    }
+
+    /**
+     * Whether parser should ignore constants or not
+     * 
+     * @return bool
+     */
+    public static function ignoreConstants()
+    {
+        return self::$_ignoreConstants;
+    }
+
+    /**
+     * Get callback for decoding YAML
+     * 
+     * @return callable
+     */
+    public function getYamlDecoder() 
+    {
+        return $this->_yamlDecoder;
+    }
+
+    /**
+     * Set callback for decoding YAML
+     * 
+     * @param  $yamlDecoder the decoder to set
+     * @return Zend_Config_Yaml
+     */
+    public function setYamlDecoder($yamlDecoder) 
+    {
+        if (!is_callable($yamlDecoder)) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception('Invalid parameter to setYamlDecoder() - must be callable');
+        }
+        
+        $this->_yamlDecoder = $yamlDecoder;
+        return $this;
+    }
+
+    /**
+     * Loads the section $section from the config file encoded as YAML
+     *
+     * Sections are defined as properties of the main object
+     *
+     * In order to extend another section, a section defines the "_extends"
+     * property having a value of the section name from which the extending
+     * section inherits values.
+     *
+     * Note that the keys in $section will override any keys of the same
+     * name in the sections that have been included via "_extends".
+     *
+     * Options may include:
+     * - allow_modifications: whether or not the config object is mutable
+     * - skip_extends: whether or not to skip processing of parent configuration
+     * - yaml_decoder: a callback to use to decode the Yaml source
+     *
+     * @param  string  $yaml     YAML file to process
+     * @param  mixed   $section Section to process
+     * @param  boolean $options Whether modifiacations are allowed at runtime
+     */
+    public function __construct($yaml, $section = null, $options = false)
+    {
+        if (empty($yaml)) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception('Filename is not set');
+        }
+
+        $ignoreConstants    = $staticIgnoreConstants = self::ignoreConstants();
+        $allowModifications = false;
+        if (is_bool($options)) {
+            $allowModifications = $options;
+        } elseif (is_array($options)) {
+            foreach ($options as $key => $value) {
+                switch (strtolower($key)) {
+                    case 'allow_modifications':
+                    case 'allowmodifications':
+                        $allowModifications = (bool) $value;
+                        break;
+                    case 'skip_extends':
+                    case 'skipextends':
+                        $this->_skipExtends = (bool) $value;
+                        break;
+                    case 'ignore_constants':
+                    case 'ignoreconstants':
+                        $ignoreConstants = (bool) $value;
+                        break;
+                    case 'yaml_decoder':
+                    case 'yamldecoder':
+                        $this->setYamlDecoder($value);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        // Suppress warnings and errors while loading file
+        set_error_handler(array($this, '_loadFileErrorHandler'));
+        $yaml = file_get_contents($yaml);
+        restore_error_handler();
+
+        // Check if there was a error while loading file
+        if ($this->_loadFileErrorStr !== null) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception($this->_loadFileErrorStr);
+        }
+
+        // Override static value for ignore_constants if provided in $options
+        self::setIgnoreConstants($ignoreConstants);
+
+        // Parse YAML
+        $config = call_user_func($this->getYamlDecoder(), $yaml);
+
+        // Reset original static state of ignore_constants
+        self::setIgnoreConstants($staticIgnoreConstants);
+        
+        if (null === $config) {
+            // decode failed
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception("Error parsing YAML data");
+        }
+
+        if (null === $section) {
+            $dataArray = array();
+            foreach ($config as $sectionName => $sectionData) {
+                $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
+            }
+            parent::__construct($dataArray, $allowModifications);
+        } elseif (is_array($section)) {
+            $dataArray = array();
+            foreach ($section as $sectionName) {
+                if (!isset($config->$sectionName)) {
+                    require_once 'Zend/Config/Exception.php';
+                    throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+                }
+
+                $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
+            }
+            parent::__construct($dataArray, $allowModifications);
+        } else {
+            if (!isset($config[$section])) {
+                require_once 'Zend/Config/Exception.php';
+                throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+            }
+
+            $dataArray = $this->_processExtends($config, $section);
+            if (!is_array($dataArray)) {
+                // Section in the yaml data contains just one top level string
+                $dataArray = array($section => $dataArray);
+            }
+            parent::__construct($dataArray, $allowModifications);
+        }
+
+        $this->_loadedSection = $section;
+    }
+
+    /**
+     * Helper function to process each element in the section and handle
+     * the "_extends" inheritance attribute.
+     *
+     * @param  array            $data Data array to process
+     * @param  string           $section Section to process
+     * @param  array            $config  Configuration which was parsed yet
+     * @return array
+     * @throws Zend_Config_Exception When $section cannot be found
+     */
+    protected function _processExtends(array $data, $section, array $config = array())
+    {
+        if (!isset($data[$section])) {
+            require_once 'Zend/Config/Exception.php';
+            throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+        }
+
+        $thisSection  = $data[$section];
+
+        if (is_array($thisSection) && isset($thisSection[self::EXTENDS_NAME])) {
+            $this->_assertValidExtend($section, $thisSection[self::EXTENDS_NAME]);
+
+            if (!$this->_skipExtends) {
+                $config = $this->_processExtends($data, $thisSection[self::EXTENDS_NAME], $config);
+            }
+            unset($thisSection[self::EXTENDS_NAME]);
+        }
+
+        $config = $this->_arrayMergeRecursive($config, $thisSection);
+
+        return $config;
+    }
+    
+    /**
+     * Very dumb YAML parser
+     * 
+     * Until we have Zend_Yaml...
+     * 
+     * @param  string $yaml YAML source
+     * @return array Decoded data
+     */
+    public static function decode($yaml)
+    {
+        $lines = explode("\n", $yaml);
+        reset($lines);
+        return self::_decodeYaml(0, $lines);
+    }
+    
+    /**
+     * Service function to decode YAML
+     * 
+     * @param  int $currentIndent Current indent level
+     * @param  array $lines  YAML lines
+     * @return array|string
+     */
+    protected static function _decodeYaml($currentIndent, &$lines)
+    {
+        $config   = array();
+        $inIndent = false;
+        while (list($n, $line) = each($lines)) {
+            $lineno = $n+1;
+            if (strlen($line) == 0) {
+                continue;
+            }
+            if ($line[0] == '#') {
+                // comment
+                continue;
+            }
+            $indent = strspn($line, " ");
+
+            // line without the spaces
+            $line = trim($line);
+            if (strlen($line) == 0) {
+                continue;
+            }
+            
+            if ($indent < $currentIndent) {
+                // this level is done
+                prev($lines);
+                return $config;
+            }
+            
+            if (!$inIndent) {
+                $currentIndent = $indent;
+                $inIndent      = true; 
+            }
+            
+            if (preg_match("/(\w+):\s*(.*)/", $line, $m)) {
+                // key: value
+                if ($m[2]) {
+                    // simple key: value
+                    $value = $m[2];
+                    // Check for booleans and constants
+                    if (preg_match('/^(t(rue)?|on|y(es)?)$/i', $value)) {
+                        $value = true;
+                    } elseif (preg_match('/^(f(alse)?|off|n(o)?)$/i', $value)) {
+                        $value = false;
+                    } elseif (!self::$_ignoreConstants) {
+                        // test for constants
+                        $value = self::_replaceConstants($value);
+                    }
+                } else {
+                    // key: and then values on new lines
+                    $value = self::_decodeYaml($currentIndent + 1, $lines);
+                    if (is_array($value) && !count($value)) {
+                        $value = "";
+                    }
+                }
+                $config[$m[1]] = $value;
+            } elseif ($line[0] == "-") {
+                // item in the list:
+                // - FOO
+                if (strlen($line) > 2) {
+                    $config[] = substr($line, 2);
+                } else {
+                    $config[] = self::_decodeYaml($currentIndent + 1, $lines);
+                }
+            } else {
+                require_once 'Zend/Config/Exception.php';
+                throw new Zend_Config_Exception(sprintf(
+                    'Error parsing YAML at line %d - unsupported syntax: "%s"',
+                    $lineno, $line
+                ));
+            }
+        }
+        return $config;
+    }
+
+    /**
+     * Replace any constants referenced in a string with their values
+     * 
+     * @param  string $value 
+     * @return string
+     */
+    protected static function _replaceConstants($value)
+    {
+        foreach (self::_getConstants() as $constant) {
+            if (strstr($value, $constant)) {
+                $value = str_replace($constant, constant($constant), $value);
+            }
+        }
+        return $value;
+    }
+
+    /**
+     * Get (reverse) sorted list of defined constant names
+     * 
+     * @return array
+     */
+    protected static function _getConstants()
+    {
+        $constants = array_keys(get_defined_constants());
+        rsort($constants, SORT_STRING);
+        return $constants;
+    }
+}

+ 4 - 0
tests/Zend/Config/AllTests.php

@@ -28,7 +28,9 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
 
 require_once 'Zend/Config/Writer/AllTests.php';
 require_once 'Zend/Config/IniTest.php';
+require_once 'Zend/Config/JsonTest.php';
 require_once 'Zend/Config/XmlTest.php';
+require_once 'Zend/Config/YamlTest.php';
 
 /**
  * @category   Zend
@@ -52,7 +54,9 @@ class Zend_Config_AllTests
         $suite->addTest(Zend_Config_Writer_AllTests::suite());
 
         $suite->addTestSuite('Zend_Config_IniTest');
+        $suite->addTestSuite('Zend_Config_JsonTest');
         $suite->addTestSuite('Zend_Config_XmlTest');
+        $suite->addTestSuite('Zend_Config_YamlTest');
 
         return $suite;
     }

+ 286 - 0
tests/Zend/Config/JsonTest.php

@@ -0,0 +1,286 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(__FILE__) . '/../../TestHelper.php';
+
+/**
+ * Zend_Config_Json
+ */
+require_once 'Zend/Config/Json.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_JsonTest extends PHPUnit_Framework_TestCase
+{
+    protected $_iniFileConfig;
+    protected $_iniFileAllSectionsConfig;
+    protected $_iniFileCircularConfig;
+
+    public function setUp()
+    {
+        $this->_iniFileConfig = dirname(__FILE__) . '/_files/config.json';
+        $this->_iniFileAllSectionsConfig = dirname(__FILE__) . '/_files/allsections.json';
+        $this->_iniFileCircularConfig = dirname(__FILE__) . '/_files/circular.json';
+        $this->_iniFileMultipleInheritanceConfig = dirname(__FILE__) . '/_files/multipleinheritance.json';
+        $this->_nonReadableConfig = dirname(__FILE__) . '/_files/nonreadable.json';
+        $this->_iniFileNoSectionsConfig = dirname(__FILE__) . '/_files/nosections.json';
+        $this->_iniFileInvalid = dirname(__FILE__) . '/_files/invalid.json';
+    }
+
+    public function testLoadSingleSection()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'all');
+
+        $this->assertEquals('all', $config->hostname);
+        $this->assertEquals('live', $config->db->name);
+        $this->assertEquals('multi', $config->one->two->three);
+        $this->assertNull(@$config->nonexistent); // property doesn't exist
+    }
+
+    public function testSectionInclude()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'staging');
+
+        $this->assertEquals('', $config->debug); // only in staging
+        $this->assertEquals('thisname', $config->name); // only in all
+        $this->assertEquals('username', $config->db->user); // only in all (nested version)
+        $this->assertEquals('dbstaging', $config->db->name); // inherited and overridden
+    }
+
+    public function testTrueValues()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'debug');
+
+        $this->assertTrue($config->debug);
+        $this->assertTrue($config->values->changed);
+    }
+
+    public function testEmptyValues()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'debug');
+
+        $this->assertType('string', $config->special->no);
+        $this->assertEquals('no', $config->special->no);
+        $this->assertNull($config->special->null);
+        $this->assertFalse($config->special->false);
+    }
+
+    /**
+     * @group review
+     */
+    public function testMultiDepthExtends()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'other_staging');
+
+        $this->assertEquals('otherStaging', $config->only_in); // only in other_staging
+        $this->assertEquals('', $config->debug); // 1 level down: only in staging
+        $this->assertEquals('thisname', $config->name); // 2 levels down: only in all
+        $this->assertEquals('username', $config->db->user); // 2 levels down: only in all (nested version)
+        $this->assertEquals('staging', $config->hostname); // inherited from two to one and overridden
+        $this->assertEquals('dbstaging', $config->db->name); // inherited from two to one and overridden
+        $this->assertEquals('anotherpwd', $config->db->pass); // inherited from two to other_staging and overridden
+    }
+
+    public function testRaisesExceptionWhenSectionNotFound()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'cannot be found');
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'extendserror');
+    }
+
+    public function testRetrievesAndMergesMultipleSections()
+    {
+        $config = new Zend_Config_Json($this->_iniFileAllSectionsConfig, array('staging','other_staging'));
+
+        $this->assertEquals('otherStaging', $config->only_in);
+        $this->assertEquals('dbstaging', $config->db->name);
+
+    }
+
+    public function testCanRetrieveAllSections()
+    {
+        $config = new Zend_Config_Json($this->_iniFileAllSectionsConfig, null);
+        $this->assertEquals('otherStaging', $config->other_staging->only_in);
+        $this->assertEquals('dbstaging', $config->staging->db->name);
+    }
+
+    public function testAllowsLoadingAllSectionsOrSomeSectionsSelectively()
+    {
+        $config = new Zend_Config_Json($this->_iniFileAllSectionsConfig, null);
+        $this->assertEquals(null, $config->getSectionName());
+        $this->assertEquals(true, $config->areAllSectionsLoaded());
+
+        $config = new Zend_Config_Json($this->_iniFileAllSectionsConfig, 'all');
+        $this->assertEquals('all', $config->getSectionName());
+        $this->assertEquals(false, $config->areAllSectionsLoaded());
+
+        $config = new Zend_Config_Json($this->_iniFileAllSectionsConfig, array('staging','other_staging'));
+        $this->assertEquals(array('staging','other_staging'), $config->getSectionName());
+        $this->assertEquals(false, $config->areAllSectionsLoaded());
+    }
+
+    public function testDetectsCircularInheritance()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'circular inheritance');
+        $config = new Zend_Config_Json($this->_iniFileCircularConfig, null);
+    }
+
+    public function testRaisesErrorWhenNoFileProvided()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'not set');
+        $config = new Zend_Config_Json('','');
+    }
+
+    public function testRaisesErrorOnAttemptsToExtendMultipleSectionsAtOnce()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'Invalid');
+        $config = new Zend_Config_Json($this->_iniFileMultipleInheritanceConfig, 'multiinherit');
+    }
+
+    public function testRaisesErrorWhenSectionNotFound()
+    {
+        try {
+            $config = new Zend_Config_Json($this->_iniFileConfig,array('all', 'notthere'));
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('cannot be found', $expected->getMessage());
+        }
+
+        try {
+            $config = new Zend_Config_Json($this->_iniFileConfig,'notthere');
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('cannot be found', $expected->getMessage());
+        }
+    }
+
+   
+    public function testCanLoadConfigWithNoSections()
+    {
+        $config = new Zend_Config_Json($this->_iniFileNoSectionsConfig);
+        
+        $this->assertEquals('all', $config->hostname);
+        $this->assertEquals('two', $config->one->two);
+        $this->assertEquals('4', $config->one->three->four);
+        $this->assertEquals('5', $config->one->three->five);
+    }
+    
+    public function testRaisesExceptionOnInvalidJsonMarkup()
+    {
+        $this->setExpectedException('Zend_Json_Exception', 'Syntax error');
+        $config = new Zend_Config_Json($this->_iniFileInvalid);
+    }
+
+    public function testOptionsPassedAreHonored()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'staging', array(
+            'skipExtends'        => true,
+            'allowModifications' => true,
+            'bar'                => 'foo', // ignored
+        ));
+        $this->assertNull($config->name); // demonstrates extends were skipped
+        $config->foo = 'bar';
+        $this->assertEquals('bar', $config->foo); // demonstrates modifications were made
+    }
+
+    public function testZf2StyleOptionsAreHonored()
+    {
+        $config = new Zend_Config_Json($this->_iniFileConfig, 'staging', array(
+            'skip_extends'        => true,
+            'allow_modifications' => true,
+            'bar'                 => 'foo', // ignored
+        ));
+        $this->assertNull($config->name); // demonstrates extends were skipped
+        $config->foo = 'bar';
+        $this->assertEquals('bar', $config->foo); // demonstrates modifications were made
+    }
+
+    public function testAllowsPassingJsonStringsToConstructor()
+    {
+        $json =<<<EOJ
+{"all":{"foo":"bar"},"staging":{"_extends":"all","bar":"baz"},"debug":{"_extends":"all","debug":true}}
+EOJ;
+        $config = new Zend_Config_Json($json, 'debug');
+        $this->assertTrue($config->debug);
+        $this->assertEquals('bar', $config->foo);
+        $this->assertNull($config->bar);
+    }
+
+    public function testProcessesSectionsWithSingleValues()
+    {
+        $json = '{"all":"values"}';
+        $config = new Zend_Config_Json($json, 'all');
+        $this->assertEquals('values', $config->all);
+    }
+
+    public function testReplacesConstantNamesWithValuesByDefault()
+    {
+        if (!defined('ZEND_CONFIG_JSON_ENV')) {
+            define('ZEND_CONFIG_JSON_ENV', 'testing');
+        }
+        if (!defined('ZEND_CONFIG_JSON_ENV_PATH')) {
+            define('ZEND_CONFIG_JSON_ENV_PATH', dirname(__FILE__));
+        }
+        if (!defined('ZEND_CONFIG_JSON_ENV_INT')) {
+            define('ZEND_CONFIG_JSON_ENV_INT', 42);
+        }
+        $json = '{"env":"ZEND_CONFIG_JSON_ENV","path":"ZEND_CONFIG_JSON_ENV_PATH/tests","int":ZEND_CONFIG_JSON_ENV_INT}';
+        $config = new Zend_Config_Json($json);
+        $this->assertEquals(ZEND_CONFIG_JSON_ENV, $config->env);
+        $this->assertEquals(ZEND_CONFIG_JSON_ENV_PATH . '/tests', $config->path);
+        $this->assertEquals(ZEND_CONFIG_JSON_ENV_INT, $config->int);
+    }
+
+    public function testCanIgnoreConstantsWhenParsing()
+    {
+        if (!defined('ZEND_CONFIG_JSON_ENV')) {
+            define('ZEND_CONFIG_JSON_ENV', 'testing');
+        }
+        $json = '{"env":"ZEND_CONFIG_JSON_ENV"}';
+        $config = new Zend_Config_Json($json, null, array('ignore_constants' => true));
+        $this->assertEquals('ZEND_CONFIG_JSON_ENV', $config->env);
+    }
+
+    public function testIgnoringConstantsCanLeadToParseErrors()
+    {
+        if (!defined('ZEND_CONFIG_JSON_ENV')) {
+            define('ZEND_CONFIG_JSON_ENV', 'testing');
+        }
+        if (!defined('ZEND_CONFIG_JSON_ENV_PATH')) {
+            define('ZEND_CONFIG_JSON_ENV_PATH', dirname(__FILE__));
+        }
+        if (!defined('ZEND_CONFIG_JSON_ENV_INT')) {
+            define('ZEND_CONFIG_JSON_ENV_INT', 42);
+        }
+        $json = '{"env":"ZEND_CONFIG_JSON_ENV","path":"ZEND_CONFIG_JSON_ENV_PATH/tests","int":ZEND_CONFIG_JSON_ENV_INT}';
+
+        $this->setExpectedException('Zend_Json_Exception', 'Syntax');
+        $config = new Zend_Config_Json($json, null, array('ignore_constants' => true));
+    }
+}

+ 4 - 0
tests/Zend/Config/Writer/AllTests.php

@@ -30,7 +30,9 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
 
 require_once 'Zend/Config/Writer/ArrayTest.php';
 require_once 'Zend/Config/Writer/IniTest.php';
+require_once 'Zend/Config/Writer/JsonTest.php';
 require_once 'Zend/Config/Writer/XmlTest.php';
+require_once 'Zend/Config/Writer/YamlTest.php';
 
 /**
  * @category   Zend
@@ -53,7 +55,9 @@ class Zend_Config_Writer_AllTests
 
         $suite->addTestSuite('Zend_Config_Writer_ArrayTest');
         $suite->addTestSuite('Zend_Config_Writer_IniTest');
+        $suite->addTestSuite('Zend_Config_Writer_JsonTest');
         $suite->addTestSuite('Zend_Config_Writer_XmlTest');
+        $suite->addTestSuite('Zend_Config_Writer_YamlTest');
 
         return $suite;
     }

+ 176 - 0
tests/Zend/Config/Writer/JsonTest.php

@@ -0,0 +1,176 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(__FILE__) . '/../../../TestHelper.php';
+
+/**
+ * Zend_Config
+ */
+require_once 'Zend/Config.php';
+
+/**
+ * Zend_Config_Json
+ */
+require_once 'Zend/Config/Json.php';
+
+/**
+ * Zend_Config_Writer_Json
+ */
+require_once 'Zend/Config/Writer/Json.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_Writer_JsonTest extends PHPUnit_Framework_TestCase
+{
+    protected $_tempName;
+    
+    public function setUp()
+    {
+        $this->_tempName = tempnam(dirname(__FILE__) . '/temp', 'tmp');
+    }
+    
+    public function tearDown()
+    {
+        @unlink($this->_tempName);
+    }
+    
+    public function testNoFilenameSet()
+    {
+        $writer = new Zend_Config_Writer_Json(array('config' => new Zend_Config(array())));
+        
+        try {
+            $writer->write();
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('No filename was set', $expected->getMessage());
+        }
+    }
+    
+    public function testNoConfigSet()
+    {
+        $writer = new Zend_Config_Writer_Json(array('filename' => $this->_tempName));
+        
+        try {
+            $writer->write();
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('No config was set', $expected->getMessage());
+        }
+    }
+    
+    public function testFileNotWritable()
+    {
+        $writer = new Zend_Config_Writer_Json(array('config' => new Zend_Config(array()), 'filename' => '/../../../'));
+        
+        try {
+            $writer->write();
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('Could not write to file', $expected->getMessage());
+        }
+    }
+    
+    public function testWriteAndRead()
+    {
+        $config = new Zend_Config(array('default' => array('test' => 'foo')));
+
+        $writer = new Zend_Config_Writer_Json(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+                
+        $config = new Zend_Config_Json($this->_tempName, null);
+        
+        $this->assertEquals('foo', $config->default->test);
+    }
+    
+    public function testNoSection()
+    {
+        $config = new Zend_Config(array('test' => 'foo', 'test2' => array('test3' => 'bar')));
+
+        $writer = new Zend_Config_Writer_Json(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+                
+        $config = new Zend_Config_Json($this->_tempName);
+        
+        $this->assertEquals('foo', $config->test);
+        $this->assertEquals('bar', $config->test2->test3);
+    }
+    
+    public function testWriteAndReadOriginalFile()
+    {
+        $config = new Zend_Config_Json(dirname(__FILE__) . '/files/allsections.json', null, array('skip_extends' => true));
+
+        $writer = new Zend_Config_Writer_Json(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+           
+        $config = new Zend_Config_Json($this->_tempName, null);       
+        $this->assertEquals('multi', $config->staging->one->two->three, var_export($config->toArray(), 1));
+
+        $config = new Zend_Config_Json($this->_tempName, null, array('skip_extends' => true));
+        $this->assertFalse(isset($config->staging->one));
+    }
+    
+    
+    public function testWriteAndReadSingleSection()
+    {
+        $config = new Zend_Config_Json(dirname(__FILE__) . '/files/allsections.json', 'staging', array('skip_extends' => true));
+
+        $writer = new Zend_Config_Writer_Json(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+                
+        $config = new Zend_Config_Json($this->_tempName, null);
+        
+        $this->assertEquals('staging', $config->staging->hostname);
+        $this->assertEquals('', $config->staging->debug);
+        $this->assertEquals(null, @$config->production);
+    }
+    
+    public function testArgumentOverride()
+    {
+        $config = new Zend_Config(array('default' => array('test' => 'foo')));
+
+        $writer = new Zend_Config_Writer_Json();
+        $writer->write($this->_tempName, $config);
+        
+        $config = new Zend_Config_Json($this->_tempName, null);
+        
+        $this->assertEquals('foo', $config->default->test);
+    }
+
+    public function testCanWritePrettyPrintedVersion()
+    {
+        $config = new Zend_Config_Json(dirname(__FILE__) . '/files/allsections-pretty.json');
+
+        $writer = new Zend_Config_Writer_Json(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->setPrettyPrint(true);
+        $writer->write();
+        $testOutput     = file_get_contents($this->_tempName);
+        $this->assertRegExp('/^\s+/m', $testOutput);
+    }
+}

+ 165 - 0
tests/Zend/Config/Writer/YamlTest.php

@@ -0,0 +1,165 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(__FILE__) . '/../../../TestHelper.php';
+
+/**
+ * Zend_Config
+ */
+require_once 'Zend/Config.php';
+
+/**
+ * Zend_Config_Yaml
+ */
+require_once 'Zend/Config/Yaml.php';
+
+/**
+ * Zend_Config_Writer_Yaml
+ */
+require_once 'Zend/Config/Writer/Yaml.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Config_Writer_YamlTest extends PHPUnit_Framework_TestCase
+{
+    protected $_tempName;
+    
+    public function setUp()
+    {
+        $this->_tempName = tempnam(dirname(__FILE__) . '/temp', 'tmp');
+    }
+    
+    public function tearDown()
+    {
+        @unlink($this->_tempName);
+    }
+    
+    public function testNoFilenameSet()
+    {
+        $writer = new Zend_Config_Writer_Yaml(array('config' => new Zend_Config(array())));
+        
+        try {
+            $writer->write();
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('No filename was set', $expected->getMessage());
+        }
+    }
+    
+    public function testNoConfigSet()
+    {
+        $writer = new Zend_Config_Writer_Yaml(array('filename' => $this->_tempName));
+        
+        try {
+            $writer->write();
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('No config was set', $expected->getMessage());
+        }
+    }
+    
+    public function testFileNotWritable()
+    {
+        $writer = new Zend_Config_Writer_Yaml(array('config' => new Zend_Config(array()), 'filename' => '/../../../'));
+        
+        try {
+            $writer->write();
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('Could not write to file', $expected->getMessage());
+        }
+    }
+    
+    public function testWriteAndRead()
+    {
+        $config = new Zend_Config(array('default' => array('test' => 'foo')));
+
+        $writer = new Zend_Config_Writer_Yaml(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+                
+        $config = new Zend_Config_Yaml($this->_tempName, null);
+        
+        $this->assertEquals('foo', $config->default->test);
+    }
+    
+    public function testNoSection()
+    {
+        $config = new Zend_Config(array('test' => 'foo', 'test2' => array('test3' => 'bar')));
+
+        $writer = new Zend_Config_Writer_Yaml(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+                
+        $config = new Zend_Config_Yaml($this->_tempName, null);
+        
+        $this->assertEquals('foo', $config->test);
+        $this->assertEquals('bar', $config->test2->test3);
+    }
+    
+    public function testWriteAndReadOriginalFile()
+    {
+        $config = new Zend_Config_Yaml(dirname(__FILE__) . '/files/allsections.yaml', null, array('skip_extends' => true));
+
+        $writer = new Zend_Config_Writer_Yaml(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+           
+        $config = new Zend_Config_Yaml($this->_tempName, null);       
+        $this->assertEquals('multi', $config->staging->one->two->three, var_export($config->toArray(), 1));
+
+        $config = new Zend_Config_Yaml($this->_tempName, null, array('skip_extends' => true));
+        $this->assertFalse(isset($config->staging->one));
+    }
+    
+    
+    public function testWriteAndReadSingleSection()
+    {
+        $config = new Zend_Config_Yaml(dirname(__FILE__) . '/files/allsections.yaml', 'staging', array('skip_extends' => true));
+
+        $writer = new Zend_Config_Writer_Yaml(array('config' => $config, 'filename' => $this->_tempName));
+        $writer->write();
+                
+        $config = new Zend_Config_Yaml($this->_tempName, null);
+        
+        $this->assertEquals('staging', $config->staging->hostname);
+        $this->assertEquals('', $config->staging->debug);
+        $this->assertEquals(null, @$config->production);
+    }
+    
+    public function testArgumentOverride()
+    {
+        $config = new Zend_Config(array('default' => array('test' => 'foo')));
+
+        $writer = new Zend_Config_Writer_Yaml();
+        $writer->write($this->_tempName, $config);
+        
+        $config = new Zend_Config_Yaml($this->_tempName, null);
+        
+        $this->assertEquals('foo', $config->default->test);
+    }
+}

+ 47 - 0
tests/Zend/Config/Writer/files/allsections-pretty.json

@@ -0,0 +1,47 @@
+{
+	"all":{
+		"hostname":"all",
+		"name":"thisname",
+		"db":{
+			"host":"127.0.0.1",
+			"user":"username",
+			"pass":"password",
+			"name":"live"
+		},
+		"one":{
+			"two":{
+				"three":"multi"
+			}
+		}
+	},
+	"staging":{
+		"_extends":"all",
+		"hostname":"staging",
+		"db":{
+			"name":"dbstaging"
+		},
+		"debug":false
+	},
+	"debug":{
+		"_extends":"all",
+		"hostname":"debug",
+		"db":{
+			"name":"dbdebug"
+		},
+		"debug":true,
+		"values":{
+			"changed":true
+		},
+		"special":{
+			"no":"no",
+			"null":null,
+			"false":false
+		}
+	},
+	"other_staging":{
+		"only_in":"otherStaging",
+		"db":{
+			"pass":"anotherpwd"
+		}
+	}
+}

+ 1 - 0
tests/Zend/Config/Writer/files/allsections.json

@@ -0,0 +1 @@
+{"all":{"hostname":"all","name":"thisname","db":{"host":"127.0.0.1","user":"username","pass":"password","name":"live"},"one":{"two":{"three":"multi"}}},"staging":{"_extends":"all","hostname":"staging","db":{"name":"dbstaging"},"debug":false},"debug":{"_extends":"all","hostname":"debug","db":{"name":"dbdebug"},"debug":true,"values":{"changed":true},"special":{"no":"no","null":null,"false":false}},"other_staging":{"only_in":"otherStaging","db":{"pass":"anotherpwd"}}}

+ 36 - 0
tests/Zend/Config/Writer/files/allsections.yaml

@@ -0,0 +1,36 @@
+all:
+    hostname: all
+    name: thisname
+    db:
+        host: 127.0.0.1
+        user: username
+        pass: password
+        name: live
+    one:
+        two:
+            three: multi
+
+staging:
+    _extends: all
+    hostname: staging
+    db:
+        name: dbstaging
+    debug: false
+
+debug:
+    _extends: all
+    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

+ 315 - 0
tests/Zend/Config/YamlTest.php

@@ -0,0 +1,315 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: IniTest.php 18950 2009-11-12 15:37:56Z alexander $
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(__FILE__) . '/../../TestHelper.php';
+
+/**
+ * Zend_Config_Ini
+ */
+require_once 'Zend/Config/Yaml.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Config
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Config
+ */
+class Zend_Config_YamlTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->_iniFileConfig             = dirname(__FILE__) . '/_files/config.yaml';
+        $this->_iniFileAllSectionsConfig  = dirname(__FILE__) . '/_files/allsections.yaml';
+        $this->_iniFileCircularConfig     = dirname(__FILE__) . '/_files/circular.yaml';
+        $this->_nonReadableConfig         = dirname(__FILE__) . '/_files/nonreadable.yaml';
+        $this->_iniFileInvalid            = dirname(__FILE__) . '/_files/invalid.yaml';
+        $this->_iniFileSameNameKeysConfig = dirname(__FILE__) . '/_files/array.yaml';
+        $this->_badIndentationConfig      = dirname(__FILE__) . '/_files/badindentation.yaml';
+        $this->_booleansConfig            = dirname(__FILE__) . '/_files/booleans.yaml';
+        $this->_constantsConfig           = dirname(__FILE__) . '/_files/constants.yaml';
+    }
+
+    public function testLoadSingleSection()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileConfig, 'all');
+
+        $this->assertEquals('all', $config->hostname);
+        $this->assertEquals('live', $config->db->name);
+        $this->assertEquals('multi', $config->one->two->three);
+        $this->assertNull(@$config->nonexistent); // property doesn't exist
+    }
+
+    public function testSectionInclude()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileConfig, 'staging');
+
+        $this->assertEquals('', $config->debug); // only in staging
+        $this->assertEquals('thisname', $config->name); // only in all
+        $this->assertEquals('username', $config->db->user); // only in all (nested version)
+        $this->assertEquals('staging', $config->hostname); // inherited and overridden
+        $this->assertEquals('dbstaging', $config->db->name); // inherited and overridden
+    }
+
+    public function testTrueValues()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileConfig, 'debug');
+
+        $this->assertType('string', $config->debug);
+        $this->assertEquals('1', $config->debug);
+        $this->assertType('string', $config->values->changed);
+        $this->assertEquals('1', $config->values->changed);
+    }
+
+    public function testEmptyValues()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileConfig, 'debug');
+
+        $this->assertType('string', $config->special->no);
+        $this->assertEquals('', $config->special->no);
+        $this->assertType('string', $config->special->null);
+        $this->assertEquals('', $config->special->null);
+        $this->assertType('string', $config->special->false);
+        $this->assertEquals('', $config->special->false);
+    }
+
+    public function testMultiDepthExtends()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileConfig, 'other_staging');
+
+        $this->assertEquals('otherStaging', $config->only_in); // only in other_staging
+        $this->assertEquals('', $config->debug); // 1 level down: only in staging
+        $this->assertEquals('thisname', $config->name); // 2 levels down: only in all
+        $this->assertEquals('username', $config->db->user); // 2 levels down: only in all (nested version)
+        $this->assertEquals('staging', $config->hostname); // inherited from two to one and overridden
+        $this->assertEquals('dbstaging', $config->db->name); // inherited from two to one and overridden
+        $this->assertEquals('anotherpwd', $config->db->pass); // inherited from two to other_staging and overridden
+    }
+
+    public function testErrorNoExtendsSection()
+    {
+        try {
+            $config = new Zend_Config_Yaml($this->_iniFileConfig, 'extendserror');
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('cannot be found', $expected->getMessage());
+        }
+    }
+
+    public function testZF413_MultiSections()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, array('staging','other_staging'));
+
+        $this->assertEquals('otherStaging', $config->only_in);
+        $this->assertEquals('staging', $config->hostname);
+
+    }
+
+    public function testZF413_AllSections()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, null);
+        $this->assertEquals('otherStaging', $config->other_staging->only_in);
+        $this->assertEquals('staging', $config->staging->hostname);
+    }
+
+    public function testZF414()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, null);
+        $this->assertEquals(null, $config->getSectionName());
+        $this->assertEquals(true, $config->areAllSectionsLoaded());
+
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, 'all');
+        $this->assertEquals('all', $config->getSectionName());
+        $this->assertEquals(false, $config->areAllSectionsLoaded());
+
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, array('staging','other_staging'));
+        $this->assertEquals(array('staging','other_staging'), $config->getSectionName());
+        $this->assertEquals(false, $config->areAllSectionsLoaded());
+    }
+
+    public function testZF415()
+    {
+        try {
+            $config = new Zend_Config_Yaml($this->_iniFileCircularConfig, null);
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('circular inheritance', $expected->getMessage());
+        }
+    }
+
+    public function testErrorNoFile()
+    {
+        try {
+            $config = new Zend_Config_Yaml('','');
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('Filename is not set', $expected->getMessage());
+        }
+    }
+
+    public function testErrorNoSectionFound()
+    {
+        try {
+            $config = new Zend_Config_Yaml($this->_iniFileConfig,array('all', 'notthere'));
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('cannot be found', $expected->getMessage());
+        }
+
+        try {
+            $config = new Zend_Config_Yaml($this->_iniFileConfig,'notthere');
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertContains('cannot be found', $expected->getMessage());
+        }
+
+    }
+
+    public function testZF3196_InvalidIniFile()
+    {
+        try {
+            $config = new Zend_Config_Yaml($this->_iniFileInvalid);
+            $this->fail('An expected Zend_Config_Exception has not been raised');
+        } catch (Zend_Config_Exception $expected) {
+            $this->assertRegexp('/(Error parsing|syntax error, unexpected)/', $expected->getMessage());
+        }
+
+    }
+    
+    public function testZF2285_MultipleKeysOfTheSameName()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileSameNameKeysConfig, null);
+        $this->assertEquals('2a', $config->one->two->{0});
+        $this->assertEquals('2b', $config->one->two->{1});
+        $this->assertEquals('4', $config->three->four->{1});
+        $this->assertEquals('5', $config->three->four->{0}->five);
+    }
+
+    public function testZF2437_ArraysWithMultipleChildren()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileSameNameKeysConfig, null);
+        $this->assertEquals('1', $config->six->seven->{0}->eight);
+        $this->assertEquals('2', $config->six->seven->{1}->eight);
+        $this->assertEquals('3', $config->six->seven->{2}->eight);
+        $this->assertEquals('1', $config->six->seven->{0}->nine);
+        $this->assertEquals('2', $config->six->seven->{1}->nine);
+        $this->assertEquals('3', $config->six->seven->{2}->nine);
+    }
+
+    public function testHonorsOptionsProvidedToConstructor()
+    {
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, 'debug', array(
+            'allow_modifications' => true,
+            'skip_extends'        => true,
+            'yaml_decoder'        => array('Zend_Config_Yaml', 'decode'),
+            'foo'                 => 'bar', // ignored
+        ));
+        $this->assertNull($config->name); // verifies extends were skipped
+        $config->foo = 'bar';
+        $this->assertEquals('bar', $config->foo); // verifies allows modifications
+    }
+
+    public function testConstructorRaisesExceptionWhenUnableToLoadFile()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'file_get_contents');
+        $config = new Zend_Config_Yaml('__foo__');
+    }
+
+    public function testBadIndentationRaisesException()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'unsupported syntax');
+        $config = new Zend_Config_Yaml($this->_badIndentationConfig, 'all');
+    }
+
+    public function testPassingBadYamlDecoderRaisesException()
+    {
+        $this->setExpectedException('Zend_Config_Exception', 'must be callable');
+        $config = new Zend_Config_Yaml($this->_iniFileAllSectionsConfig, 'debug', array(
+            'yaml_decoder' => '__foo__',
+        ));
+    }
+
+    public function testParsesBooleansAccordingToOneDotOneSpecification()
+    {
+        $config = new Zend_Config_Yaml($this->_booleansConfig, 'production');
+
+        $this->assertTrue($config->usingLowerCasedYes);
+        $this->assertTrue($config->usingTitleCasedYes);
+        $this->assertTrue($config->usingCapitalYes);
+        $this->assertTrue($config->usingLowerY);
+        $this->assertTrue($config->usingUpperY);
+
+        $this->assertFalse($config->usingLowerCasedNo);
+        $this->assertFalse($config->usingTitleCasedNo);
+        $this->assertFalse($config->usingCapitalNo);
+        $this->assertFalse($config->usingLowerN);
+        $this->assertFalse($config->usingUpperN);
+
+        $this->assertTrue($config->usingLowerCasedTrue);
+        $this->assertTrue($config->usingTitleCasedTrue);
+        $this->assertTrue($config->usingCapitalTrue);
+
+        $this->assertFalse($config->usingLowerCasedFalse);
+        $this->assertFalse($config->usingTitleCasedFalse);
+        $this->assertFalse($config->usingCapitalFalse);
+
+        $this->assertTrue($config->usingLowerCasedOn);
+        $this->assertTrue($config->usingTitleCasedOn);
+        $this->assertTrue($config->usingCapitalOn);
+
+        $this->assertFalse($config->usingLowerCasedOff);
+        $this->assertFalse($config->usingTitleCasedOff);
+        $this->assertFalse($config->usingCapitalOff);
+    }
+
+    public function testHonorsPhpConstants()
+    {
+        if (!defined('ZEND_CONFIG_YAML_ENV')) {
+            define('ZEND_CONFIG_YAML_ENV', 'testing');
+        }
+        if (!defined('ZEND_CONFIG_YAML_ENV_PATH')) {
+            define('ZEND_CONFIG_YAML_ENV_PATH', dirname(__FILE__));
+        }
+        $config = new Zend_Config_Yaml($this->_constantsConfig, 'production');
+        $this->assertEquals(ZEND_CONFIG_YAML_ENV, $config->env);
+        $this->assertEquals(ZEND_CONFIG_YAML_ENV_PATH . '/test/this', $config->path);
+    }
+
+    public function testAllowsIgnoringConstantStrings()
+    {
+        if (!defined('ZEND_CONFIG_YAML_ENV')) {
+            define('ZEND_CONFIG_YAML_ENV', 'testing');
+        }
+        if (!defined('ZEND_CONFIG_YAML_ENV_PATH')) {
+            define('ZEND_CONFIG_YAML_ENV_PATH', dirname(__FILE__));
+        }
+        $config = new Zend_Config_Yaml(
+            $this->_constantsConfig, 'production', array('ignore_constants' => true)
+        );
+        $this->assertEquals('ZEND_CONFIG_YAML_ENV', $config->env);
+        $this->assertEquals('ZEND_CONFIG_YAML_ENV_PATH/test/this', $config->path);
+    }
+}

+ 1 - 0
tests/Zend/Config/_files/allsections.json

@@ -0,0 +1 @@
+{"all":{"hostname":"all","name":"thisname","db":{"host":"127.0.0.1","user":"username","pass":"password","name":"live"},"one":{"two":{"three":"multi"}}},"staging":{"_extends":"all","db":{"name":"dbstaging"},"debug":false},"debug":{"_extends":"all","db":{"name":"dbdebug"},"debug":true,"values":{"changed":true},"special":{"no":"no","null":null,"false":false}},"other_staging":{"only_in":"otherStaging","db":{"pass":"anotherpwd"}}}

+ 35 - 0
tests/Zend/Config/_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

+ 22 - 0
tests/Zend/Config/_files/array.yaml

@@ -0,0 +1,22 @@
+one: 
+  two: 
+    - 2a
+    - 2b
+three: 
+  four: 
+    - 
+      five: 5
+    - 4
+    - 
+      five: 5
+six: 
+  seven: 
+    - 
+      eight: 1
+      nine: 1
+    - 
+      eight: 2
+      nine: 2
+    - 
+      eight: 3
+      nine: 3

+ 6 - 0
tests/Zend/Config/_files/badindentation.yaml

@@ -0,0 +1,6 @@
+all: 
+  hostname: all
+  name: thisname
+  foo:
+    bar
+     baz

+ 29 - 0
tests/Zend/Config/_files/booleans.yaml

@@ -0,0 +1,29 @@
+production:
+    usingLowerCasedYes: yes
+    usingTitleCasedYes: Yes
+    usingCapitalYes: YES
+    usingLowerY: y
+    usingUpperY: Y
+
+    usingLowerCasedNo: no
+    usingTitleCasedNo: No
+    usingCapitalNo: NO
+    usingLowerN: n
+    usingUpperN: N
+
+    usingLowerCasedTrue: true
+    usingTitleCasedTrue: True
+    usingCapitalTrue: TRUE
+
+    usingLowerCasedFalse: false
+    usingTitleCasedFalse: False
+    usingCapitalFalse: FALSE
+
+    usingLowerCasedOn: on
+    usingTitleCasedOn: On
+    usingCapitalOn: ON
+
+    usingLowerCasedOff: off
+    usingTitleCasedOff: Off
+    usingCapitalOff: OFF
+

+ 1 - 0
tests/Zend/Config/_files/circular.json

@@ -0,0 +1 @@
+{"A":{"_extends":"C","someKey":"value_A"},"B":{"_extends":"A","someKey":"value_B"},"C":{"_extends":"B","someKey":"value_C"}}

+ 9 - 0
tests/Zend/Config/_files/circular.yaml

@@ -0,0 +1,9 @@
+A: 
+  someKey: value_A
+  _extends: C
+B: 
+  someKey: value_B
+  _extends: A
+C: 
+  someKey: value_C
+  _extends: B

+ 1 - 0
tests/Zend/Config/_files/config.json

@@ -0,0 +1 @@
+{"all":{"hostname":"all","name":"thisname","db":{"host":"127.0.0.1","user":"username","pass":"password","name":"live"},"one":{"two":{"three":"multi"}}},"staging":{"_extends":"all","hostname":"staging","db":{"name":"dbstaging"},"debug":false},"debug":{"_extends":"all","db":{"name":"dbdebug"},"debug":true,"values":{"changed":true},"special":{"no":"no","null":null,"false":false}},"other_staging":{"_extends":"staging","only_in":"otherStaging","db":{"pass":"anotherpwd"}},"extendserror":{"_extends":"nonexistent","testing":123}}

+ 37 - 0
tests/Zend/Config/_files/config.yaml

@@ -0,0 +1,37 @@
+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: 
+  _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
+  _extends: staging
+extendserror: 
+  testing: 123
+  _extends: notexistent

+ 3 - 0
tests/Zend/Config/_files/constants.yaml

@@ -0,0 +1,3 @@
+production:
+    env:  ZEND_CONFIG_YAML_ENV
+    path: ZEND_CONFIG_YAML_ENV_PATH/test/this

+ 1 - 0
tests/Zend/Config/_files/invalid.json

@@ -0,0 +1 @@
+{"default":{"foo":/("bar)}}

+ 2 - 0
tests/Zend/Config/_files/invalid.yaml

@@ -0,0 +1,2 @@
+yaml: 
+  what?

+ 1 - 0
tests/Zend/Config/_files/multipleinheritance.json

@@ -0,0 +1 @@
+{"one":{"one":1},"two":{"two":2},"multiinherit":{"_extends":["one","two"]}}

+ 1 - 0
tests/Zend/Config/_files/nosections.json

@@ -0,0 +1 @@
+{"hostname":"all","one":{"two":"two","three":{"four":4,"five":5}}}