Permalink
Browse files

Initial move from Doctrine/CouchDB namespace into its own subproject.

  • Loading branch information...
0 parents commit 86c480de76fb2e0eeab976ae462b7e72c9259e5d @beberlei beberlei committed Mar 4, 2012
Showing with 3,891 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +502 −0 LICENSE
  3. +21 −0 composer.json
  4. +11 −0 composer.lock
  5. +265 −0 lib/Doctrine/CouchDB/Attachment.php
  6. +498 −0 lib/Doctrine/CouchDB/CouchDBClient.php
  7. +65 −0 lib/Doctrine/CouchDB/CouchDBException.php
  8. +88 −0 lib/Doctrine/CouchDB/HTTP/AbstractHTTPClient.php
  9. +39 −0 lib/Doctrine/CouchDB/HTTP/Client.php
  10. +19 −0 lib/Doctrine/CouchDB/HTTP/ErrorResponse.php
  11. +68 −0 lib/Doctrine/CouchDB/HTTP/HTTPException.php
  12. +82 −0 lib/Doctrine/CouchDB/HTTP/LoggingClient.php
  13. +67 −0 lib/Doctrine/CouchDB/HTTP/Response.php
  14. +260 −0 lib/Doctrine/CouchDB/HTTP/SocketClient.php
  15. +127 −0 lib/Doctrine/CouchDB/HTTP/StreamClient.php
  16. +23 −0 lib/Doctrine/CouchDB/JsonDecodeException.php
  17. +44 −0 lib/Doctrine/CouchDB/Tools/Console/Command/CompactDatabaseCommand.php
  18. +47 −0 lib/Doctrine/CouchDB/Tools/Console/Command/CompactViewCommand.php
  19. +51 −0 lib/Doctrine/CouchDB/Tools/Console/Command/MigrationCommand.php
  20. +62 −0 lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationCancelCommand.php
  21. +67 −0 lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationStartCommand.php
  22. +44 −0 lib/Doctrine/CouchDB/Tools/Console/Command/ViewCleanupCommand.php
  23. +84 −0 lib/Doctrine/CouchDB/Tools/Console/Helper/CouchDBHelper.php
  24. +71 −0 lib/Doctrine/CouchDB/Tools/Migrations/AbstractMigration.php
  25. +71 −0 lib/Doctrine/CouchDB/Utils/BulkUpdater.php
  26. +25 −0 lib/Doctrine/CouchDB/Version.php
  27. +129 −0 lib/Doctrine/CouchDB/View/AbstractQuery.php
  28. +51 −0 lib/Doctrine/CouchDB/View/DesignDocument.php
  29. +60 −0 lib/Doctrine/CouchDB/View/FolderDesignDocument.php
  30. +122 −0 lib/Doctrine/CouchDB/View/LuceneQuery.php
  31. +46 −0 lib/Doctrine/CouchDB/View/LuceneResult.php
  32. +209 −0 lib/Doctrine/CouchDB/View/Query.php
  33. +48 −0 lib/Doctrine/CouchDB/View/Result.php
  34. +24 −0 phpunit.xml.dist
  35. +38 −0 tests/Doctrine/Tests/CouchDB/CouchDBFunctionalTestCase.php
  36. +168 −0 tests/Doctrine/Tests/CouchDB/Functional/CouchDBClientTest.php
  37. +14 −0 tests/Doctrine/Tests/CouchDB/TestUtil.php
  38. +36 −0 tests/Doctrine/Tests/Models/CMS/CmsAddress.php
  39. +33 −0 tests/Doctrine/Tests/Models/CMS/CmsArticle.php
  40. +38 −0 tests/Doctrine/Tests/Models/CMS/CmsGroup.php
  41. +35 −0 tests/Doctrine/Tests/Models/CMS/CmsNode.php
  42. +81 −0 tests/Doctrine/Tests/Models/CMS/CmsUser.php
  43. +15 −0 tests/Doctrine/Tests/Models/CMS/CmsUserRights.php
  44. +16 −0 tests/Doctrine/Tests/Models/CMS/_files/rewrites.json
  45. +5 −0 tests/Doctrine/Tests/Models/CMS/_files/views/username/map.js
  46. +21 −0 tests/TestInit.php
1 .gitignore
@@ -0,0 +1 @@
+vendor
502 LICENSE
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
21 composer.json
@@ -0,0 +1,21 @@
+{
+ "name": "doctrine/couchdb",
+ "type": "library",
+ "description": "CouchDB Client",
+ "keywords": ["persistence", "couchdb"],
+ "homepage": "http://www.doctrine-project.org",
+ "license": "LGPL",
+ "authors": [
+ {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+ {"name": "Lukas Kahwe Smith", "email": "smith@pooteeweet.org"}
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "doctrine/common": ">=2.2.0"
+ },
+ "autoload": {
+ "psr-0": {
+ "Doctrine\\CouchDB": "lib/"
+ }
+ }
+}
11 composer.lock
@@ -0,0 +1,11 @@
+{
+ "hash": "a9de4852bde016fcab7c17df42f9506a",
+ "packages": [
+ {
+ "package": "doctrine/common",
+ "version": "dev-master",
+ "source-reference": "f157be8c4f1ba713730cf66066eaac2bdfa1c1cc"
+ }
+ ],
+ "aliases": []
+}
265 lib/Doctrine/CouchDB/Attachment.php
@@ -0,0 +1,265 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+
+namespace Doctrine\CouchDB;
+
+use Doctrine\CouchDB\HTTP\Client;
+use Doctrine\CouchDB\HTTP\HTTPException;
+
+/**
+ * An attachment is a special embedded document that exists inside CouchDB.
+ * It is created inside the "Attachments" object for each attachment that is found.
+ *
+ * TODO: This is a very inefficient first version implementation that saves both
+ * binary and base64 data of everything if possible to ease the API.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class Attachment
+{
+ /**
+ * Content-Type of the Attachment
+ *
+ * If this is false on putting a new attachment into the database the
+ * generic "application/octet-stream" type will be used.
+ *
+ * @var string
+ */
+ private $contentType = false;
+
+ /**
+ * Base64 Encoded tring of the Data.
+ *
+ * @var string
+ */
+ private $data;
+
+ /**
+ * @var string
+ */
+ private $binaryData;
+
+ /**
+ * This attachment only represented as stub, which means the attachment is standalone and not inline.
+ *
+ * WARNING: Never change this variable from true to false if you don't provide data.
+ * CouchDB otherwise quits with an error: {"error":"unknown_error","reason":"function_clause"}
+ *
+ * @var bool
+ */
+ private $stub = true;
+
+ /**
+ * Size of the attachment.
+ *
+ * @var int
+ */
+ private $length = 0;
+
+ /**
+ * Revision Position field of this Attachment.
+ *
+ * @var int
+ */
+ private $revpos = 0;
+
+ /**
+ * @var Client
+ */
+ private $httpClient;
+
+ /**
+ * @var string
+ */
+ private $path;
+
+ /**
+ * Get the content-type of this attachment.
+ *
+ * @return string
+ */
+ public function getContentType()
+ {
+ return $this->contentType;
+ }
+
+ /**
+ * Get the length of the base64 encoded representation of this attachment.
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ if (!$this->stub && !is_int($this->length)) {
+ $this->length = strlen($this->data);
+ }
+ return $this->length;
+ }
+
+ /**
+ * Get the raw data of this attachment.
+ *
+ * @return string
+ */
+ public function getRawData()
+ {
+ $this->lazyLoad();
+
+ return $this->binaryData;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBase64EncodedData()
+ {
+ $this->lazyLoad();
+
+ return $this->data;
+ }
+
+ /**
+ * Lazy Load Data from CouchDB if necessary
+ *
+ * @return void
+ */
+ private function lazyLoad()
+ {
+ if ($this->stub) {
+ $response = $this->httpClient->request('GET', $this->path, null, true); // raw request
+ if ($response->status != 200) {
+ throw HTTPException::fromResponse($this->path, $response);
+ }
+ $this->stub = false;
+ $this->binaryData = $response->body;
+ $this->data = \base64_encode($this->binaryData);
+ }
+ }
+
+ public function isLoaded()
+ {
+ return !$this->stub;
+ }
+
+ /**
+ * Number of times an attachment was alreaady saved with the document, indicating in which revision it was added.
+ *
+ * @return int
+ */
+ public function getRevPos()
+ {
+ return $this->revpos;
+ }
+
+ /**
+ * Attachments are special in how they need to be persisted depending on stub or not.
+ *
+ * TODO: Is this really necessary with all this special logic? Having attahments as special
+ * case without special code would be really awesome.
+ *
+ * @return string
+ */
+ public function toArray()
+ {
+ if ($this->stub) {
+ $json = array('stub' => true);
+ } else {
+ $json = array('data' => $this->getBase64EncodedData());
+ if ($this->contentType) {
+ $json['content_type'] = $this->contentType;
+ }
+ }
+ return $json;
+ }
+
+ /**
+ * @param string $binaryData
+ * @param string $base64Data
+ * @param string $contentType
+ * @param int $length
+ * @param int $revPos
+ * @param Client $httpClient
+ * @param string $path
+ */
+ final private function __construct($binaryData = null, $base64Data = null, $contentType = false, $length = false, $revPos = false, $httpClient = null, $path = null)
+ {
+ if ($binaryData || $base64Data) {
+ $this->binaryData = $binaryData;
+ $this->data = $base64Data;
+ $this->stub = false;
+ } else {
+ $this->stub = true;
+ }
+ $this->contentType = $contentType;
+ $this->length = $length;
+ $this->revpos = $revPos;
+ $this->httpClient = $httpClient;
+ $this->path = $path;
+ }
+
+ /**
+ * Create an Attachment from a string or resource of binary data.
+ *
+ * WARNING: Changes to the file handle after calling this method will *NOT* be recognized anymore.
+ *
+ * @param string|resource $data
+ * @param string $contentType
+ * @return Attachment
+ */
+ static public function createFromBinaryData($data, $contentType = false)
+ {
+ if (\is_resource($data)) {
+ $data = \stream_get_contents($data);
+ }
+
+ return new self($data, \base64_encode($data), $contentType);
+ }
+
+ /**
+ * Create an attachment from base64 data.
+ *
+ * @param string $data
+ * @param string $contentType
+ * @param int $revpos
+ * @return Attachment
+ */
+ static public function createFromBase64Data($data, $contentType = false, $revpos = false)
+ {
+ return new self(\base64_decode($data), $data, $contentType, false, $revpos);
+ }
+
+ /**
+ * Create a stub attachment that has lazy loading capabilities.
+ *
+ * @param string $contentType
+ * @param int $length
+ * @param int $revPos
+ * @param Client $httpClient
+ * @param string $path
+ * @return Attachment
+ */
+ static public function createStub($contentType, $length, $revPos, Client $httpClient, $path)
+ {
+ return new self(null, null, $contentType, $length, $revPos, $httpClient, $path);
+ }
+}
498 lib/Doctrine/CouchDB/CouchDBClient.php
@@ -0,0 +1,498 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB;
+
+use Doctrine\CouchDB\HTTP\Client;
+use Doctrine\CouchDB\HTTP\HTTPException;
+use Doctrine\CouchDB\Utils\BulkUpdater;
+use Doctrine\CouchDB\View\DesignDocument;
+
+/**
+ * CouchDB client class
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ * @author Lukas Kahwe Smith <smith@pooteeweet.org>
+ */
+class CouchDBClient
+{
+ /**
+ * Name of the CouchDB database
+ *
+ * @string
+ */
+ private $databaseName;
+
+ /**
+ * The underlying HTTP Connection of the used DocumentManager.
+ *
+ * @var Doctrine\ODM\CouchDB\HTTP\Client
+ */
+ private $httpClient;
+
+ /**
+ * CouchDB Version
+ *
+ * @var string
+ */
+ private $version = null;
+
+ static private $clients = array(
+ 'socket' => 'Doctrine\CouchDB\HTTP\SocketClient',
+ 'stream' => 'Doctrine\CouchDB\HTTP\StreamClient',
+ );
+
+ /**
+ * Factory method for CouchDBClients
+ *
+ * @param array $options
+ * @return CouchDBClient
+ */
+ static public function create(array $options)
+ {
+ if (!isset($options['dbname'])) {
+ throw new \InvalidArgumentException("'dbname' is a required option to create a CouchDBClient");
+ }
+
+ $defaults = array('type' => 'socket', 'host' => 'localhost', 'port' => 5984, 'user' => null, 'password' => null, 'ip' => null, 'logging' => false);
+ $options = array_merge($defaults, $options);
+
+ if (!isset(self::$clients[$options['type']])) {
+ throw new \InvalidArgumentException(sprintf('There is no client implementation registered for %s, valid options are %s',
+ $options['type'], array_keys(self::$clients)
+ ));
+ }
+ $connectionClass = self::$clients[$options['type']];
+ $connection = new $connectionClass($options['host'], $options['port'], $options['user'], $options['password'], $options['ip']);
+ if ($options['logging'] === true) {
+ $connection = new HTTP\LoggingClient($connection);
+ }
+ return new self($connection, $options['dbname']);
+ }
+
+ /**
+ * @param Client $client
+ * @param string $databaseName
+ */
+ public function __construct(Client $client, $databaseName)
+ {
+ $this->httpClient = $client;
+ $this->databaseName = $databaseName;
+ }
+
+ public function setHttpClient(Client $client)
+ {
+ $this->httpClient = $client;
+ }
+
+ /**
+ * @return HttpClient
+ */
+ public function getHttpClient()
+ {
+ return $this->httpClient;
+ }
+
+ public function getDatabase()
+ {
+ return $this->databaseName;
+ }
+
+ /**
+ * Let CouchDB generate an array of UUIDs.
+ *
+ * @param int $count
+ * @return array
+ */
+ public function getUuids($count = 1)
+ {
+ $count = (int)$count;
+ $response = $this->httpClient->request('GET', '/_uuids?count=' . $count);
+
+ if ($response->status != 200) {
+ throw new CouchDBException("Could not retrieve UUIDs from CouchDB.");
+ }
+
+ return $response->body['uuids'];
+ }
+
+ /**
+ * Find a document by ID and return the HTTP response.
+ *
+ * @param string $id
+ * @return Response
+ */
+ public function findDocument($id)
+ {
+ $documentPath = '/' . $this->databaseName . '/' . urlencode($id);
+ return $this->httpClient->request( 'GET', $documentPath );
+ }
+
+ /**
+ * Find many documents by passing their ids and return the HTTP response.
+ *
+ * @param array $ids
+ * @return array
+ */
+ public function findDocuments(array $ids, $limit = null, $offset = null)
+ {
+ $allDocsPath = '/' . $this->databaseName . '/_all_docs?include_docs=true';
+ if ($limit) {
+ $allDocsPath .= '&limit=' . (int)$limit;
+ }
+ if ($offset) {
+ $allDocsPath .= '&skip=' . (int)$offset;
+ }
+
+ return $this->httpClient->request('POST', $allDocsPath, json_encode(
+ array('keys' => array_values($ids)))
+ );
+ }
+
+ /**
+ * Get all documents
+ *
+ * @param int|null $limit
+ * @param string|null $startKey
+ * @return array
+ */
+ public function allDocs($limit = null, $startKey = null)
+ {
+ $allDocsPath = '/' . $this->databaseName . '/_all_docs?include_docs=true';
+ if ($limit) {
+ $allDocsPath .= '&limit=' . (int)$limit;
+ }
+ if ($startKey) {
+ $allDocsPath .= '&startkey="' . (string)$startKey.'"';
+ }
+ return $this->httpClient->request('GET', $allDocsPath);
+ }
+
+ /**
+ * Get the current version of CouchDB.
+ *
+ * @throws HTTPException
+ * @return string
+ */
+ public function getVersion()
+ {
+ if ($this->version === null) {
+ $response = $this->httpClient->request('GET', '/');
+ if ($response->status != 200) {
+ throw HTTPException::fromResponse('/', $response);
+ }
+
+ $this->version = $response->body['version'];
+ }
+ return $this->version;
+ }
+
+ /**
+ * Get all databases
+ *
+ * @throws HTTPException
+ * @return array
+ */
+ public function getAllDatabases()
+ {
+ $response = $this->httpClient->request('GET', '/_all_dbs');
+ if ($response->status != 200) {
+ throw HTTPException::fromResponse('/_all_dbs', $response);
+ }
+
+ return $response->body;
+ }
+
+ /**
+ * Create a new database
+ *
+ * @throws HTTPException
+ * @param string $name
+ * @return void
+ */
+ public function createDatabase($name)
+ {
+ $response = $this->httpClient->request('PUT', '/' . urlencode($name));
+
+ if ($response->status != 201) {
+ throw HTTPException::fromResponse('/' . urlencode($name), $response);
+ }
+ }
+
+ /**
+ * Drop a database
+ *
+ * @throws HTTPException
+ * @param string $name
+ * @return void
+ */
+ public function deleteDatabase($name)
+ {
+ $response = $this->httpClient->request('DELETE', '/' . urlencode($name));
+
+ if ($response->status != 200 && $response->status != 404) {
+ throw HTTPException::fromResponse('/' . urlencode($name), $response);
+ }
+ }
+
+ /**
+ * Get Information about a database.
+ *
+ * @param string $name
+ * @return array
+ */
+ public function getDatabaseInfo($name)
+ {
+ $response = $this->httpClient->request('GET', '/' . $this->databaseName);
+
+ if ($response->status != 200) {
+ throw HTTPException::fromResponse('/' . urlencode($name), $response);
+ }
+
+ return $response->body;
+ }
+
+ /**
+ * Get changes.
+ *
+ * @param array $params
+ * @return array
+ */
+ public function getChanges(array $params = null)
+ {
+ $path = '/' . $this->databaseName . '/_changes';
+ $response = $this->httpClient->request('GET', $path, $params);
+
+ if ($response->status != 200) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+
+ return $response->body;
+ }
+
+ /**
+ * Create a bulk updater instance.
+ *
+ * @return BulkUpdater
+ */
+ public function createBulkUpdater()
+ {
+ return new BulkUpdater($this->httpClient, $this->databaseName);
+ }
+
+ /**
+ * Execute a POST request against CouchDB inserting a new document, leaving the server to generate a uuid.
+ *
+ * @param array $data
+ * @return array<id, rev>
+ */
+ public function postDocument(array $data)
+ {
+ $path = '/' . $this->databaseName;
+ $response = $this->httpClient->request('POST', $path, json_encode($data));
+
+ if ($response->status != 201) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+
+ return array($response->body['id'], $response->body['rev']);
+ }
+
+ /**
+ * Execute a PUT request against CouchDB inserting or updating a document.
+ *
+ * @param array $data
+ * @param string $id
+ * @param string|null $rev
+ * @return array<id, rev>
+ */
+ public function putDocument($data, $id, $rev = null)
+ {
+ $data['_id'] = $id;
+ if ($rev) {
+ $data['_rev'] = $rev;
+ }
+
+ $path = '/' . $this->databaseName . '/' . urlencode($id);
+ $response = $this->httpClient->request('PUT', $path, json_encode($data));
+
+ if ($response->status != 201) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+
+ return array($response->body['id'], $response->body['rev']);
+ }
+
+ /**
+ * Delete a document.
+ *
+ * @param string $id
+ * @param string $rev
+ * @return void
+ */
+ public function deleteDocument($id, $rev)
+ {
+ $path = '/' . $this->databaseName . '/' . $id . '?rev=' . $rev;
+ $response = $this->httpClient->request('DELETE', $path);
+
+ if ($response->status != 200) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+ }
+
+ /**
+ * @param string $designDocName
+ * @param string $viewName
+ * @param DesignDocument $designDoc
+ * @return View\Query
+ */
+ public function createViewQuery($designDocName, $viewName, DesignDocument $designDoc)
+ {
+ return new View\Query($this->httpClient, $this->databaseName, $designDocName, $viewName, $designDoc);
+ }
+
+ /**
+ * Create or update a design document from the given in memory definition.
+ *
+ * @param string $designDocName
+ * @param DesignDocument $designDoc
+ * @return HTTP\Response
+ */
+ public function createDesignDocument($designDocName, DesignDocument $designDoc)
+ {
+ $data = $designDoc->getData();
+ $data['_id'] = '_design/' . $designDocName;
+
+ $response = $this->findDocument($data['_id']);
+ if ($response->status == 200) {
+ $docData = $response->body;
+ $data['_rev'] = $docData['_rev'];
+ }
+
+ return $this->httpClient->request(
+ "PUT",
+ sprintf("/%s/_design/%s", $this->databaseName, $designDocName),
+ json_encode($data)
+ );
+ }
+
+ /**
+ * GET /db/_compact
+ *
+ * Return array of data about compaction status.
+ *
+ * @return array
+ */
+ public function getCompactInfo()
+ {
+ $path = sprintf('/%s/_compact', $this->databaseName);
+ $response = $this->httpClient->request('GET', $path);
+ if ($response->status >= 400) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+ return $response->body;
+ }
+
+ /**
+ * POST /db/_compact
+ *
+ * @return array
+ */
+ public function compactDatabase()
+ {
+ $path = sprintf('/%s/_compact', $this->databaseName);
+ $response = $this->httpClient->request('POST', $path);
+ if ($response->status >= 400) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+ return $response->body;
+ }
+
+ /**
+ * POST /db/_compact/designDoc
+ *
+ * @param string $designDoc
+ * @return array
+ */
+ public function compactView($designDoc)
+ {
+ $path = sprintf('/%s/_compact/%s', $this->databaseName, $designDoc);
+ $response = $this->httpClient->request('POST', $path);
+ if ($response->status >= 400) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+ return $response->body;
+ }
+
+ /**
+ * POST /db/_view_cleanup
+ *
+ * @return array
+ */
+ public function viewCleanup()
+ {
+ $path = sprintf('/%s/_view_cleanup', $this->databaseName);
+ $response = $this->httpClient->request('POST', $path);
+ if ($response->status >= 400) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+ return $response->body;
+ }
+
+ /**
+ * POST /db/_replicate
+ *
+ * @param string $source
+ * @param string $target
+ * @param bool|null $cancel
+ * @param bool|null $continuous
+ * @param string|null $filter
+ * @param array|null $ids
+ * @param string|null $proxy
+ * @return array
+ */
+ public function replicate($source, $target, $cancel = null, $continuous = null, $filter = null, array $ids = null, $proxy = null)
+ {
+ $params = array('target' => $target, 'source' => $source);
+ if ($cancel !== null) {
+ $params['cancel'] = (bool)$cancel;
+ }
+ if ($continuous !== null) {
+ $params['continuous'] = (bool)$continuous;
+ }
+ if ($filter !== null) {
+ $params['filter'] = $filter;
+ }
+ if ($ids !== null) {
+ $params['doc_ids'] = $ids;
+ }
+ if ($proxy !== null) {
+ $params['proxy'] = $proxy;
+ }
+ $path = '/_replicate';
+ $response = $this->httpClient->request('POST', $path, json_encode($params));
+ if ($response->status >= 400) {
+ throw HTTPException::fromResponse($path, $response);
+ }
+ return $response->body;
+ }
+}
65 lib/Doctrine/CouchDB/CouchDBException.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB;
+
+/**
+ * Base exception class for package Doctrine\ODM\CouchDB
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class CouchDBException extends \Exception
+{
+
+ public static function unknownDocumentNamespace($documentNamespaceAlias)
+ {
+ return new self("Unknown Document namespace alias '$documentNamespaceAlias'.");
+ }
+
+ public static function unregisteredDesignDocument($designDocumentName)
+ {
+ return new self("No design document with name '" . $designDocumentName . "' was registered with the DocumentManager.");
+ }
+
+ public static function invalidAttachment($className, $id, $filename)
+ {
+ return new self("Trying to save invalid attachment with filename " . $filename . " in document " . $className . " with id " . $id);
+ }
+
+ public static function detachedDocumentFound($className, $id, $assocName)
+ {
+ return new self("Found a detached or new document at property " .
+ $className . "::" . $assocName. " of document with ID " . $id . ", ".
+ "but the assocation is not marked as cascade persist.");
+ }
+
+ public static function persistRemovedDocument()
+ {
+ return new self("Trying to persist document that is scheduled for removal.");
+ }
+
+ public static function luceneNotConfigured()
+ {
+ return new self("CouchDB Lucene is not configured. You have to configure the handler name to enable support for Lucene Queries.");
+ }
+}
+
88 lib/Doctrine/CouchDB/HTTP/AbstractHTTPClient.php
@@ -0,0 +1,88 @@
+<?php
+/** HTTP Client interface
+ *
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+/**
+ * Basic couch DB connection handling class
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Kore Nordmann <kore@arbitracker.org>
+ */
+abstract class AbstractHTTPClient implements Client
+{
+ /**
+ * CouchDB connection options
+ *
+ * @var array
+ */
+ protected $options = array(
+ 'host' => 'localhost',
+ 'port' => 5984,
+ 'ip' => '127.0.0.1',
+ 'timeout' => .01,
+ 'keep-alive' => true,
+ 'username' => null,
+ 'password' => null,
+ );
+
+ /**
+ * Construct a CouchDB connection
+ *
+ * Construct a CouchDB connection from basic connection parameters for one
+ * given database.
+ *
+ * @param string $host
+ * @param int $port
+ * @param string $username
+ * @param string $password
+ * @param string $ip
+ * @return void
+ */
+ public function __construct( $host = 'localhost', $port = 5984, $username = null, $password = null, $ip = null )
+ {
+ $this->options['host'] = (string) $host;
+ $this->options['port'] = (int) $port;
+ $this->options['username'] = $username;
+ $this->options['password'] = $password;
+
+ if ($ip === null) {
+ $this->options['ip'] = gethostbyname($this->options['host']);
+ } else {
+ $this->options['ip'] = $ip;
+ }
+ }
+
+ /**
+ * Set option value
+ *
+ * Set the value for an connection option. Throws an
+ * InvalidArgumentException for unknown options.
+ *
+ * @param string $option
+ * @param mixed $value
+ * @return void
+ */
+ public function setOption( $option, $value )
+ {
+ switch ( $option ) {
+ case 'keep-alive':
+ $this->options[$option] = (bool) $value;
+ break;
+
+ case 'http-log':
+ case 'password':
+ case 'username':
+ $this->options[$option] = $value;
+ break;
+
+ default:
+ throw new \InvalidArgumentException( "Unknown option $option." );
+ }
+ }
+}
+
39 lib/Doctrine/CouchDB/HTTP/Client.php
@@ -0,0 +1,39 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+interface Client
+{
+ /**
+ * Perform a request to the server and return the result
+ *
+ * Perform a request to the server and return the result converted into a
+ * Response object. If you do not expect a JSON structure, which
+ * could be converted in such a response object, set the fourth parameter to
+ * true, and you get a response object retuerned, containing the raw body.
+ *
+ * @param string $method
+ * @param string $path
+ * @param string $data
+ * @param bool $raw
+ * @return Response
+ */
+ function request( $method, $path, $data = null, $raw = false );
+}
19 lib/Doctrine/CouchDB/HTTP/ErrorResponse.php
@@ -0,0 +1,19 @@
+<?php
+/** HTTP Client interface
+ *
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+/**
+ * HTTP response
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Kore Nordmann <kore@arbitracker.org>
+ */
+class ErrorResponse extends Response
+{
+}
+
68 lib/Doctrine/CouchDB/HTTP/HTTPException.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Doctrine\CouchDB\HTTP;
+
+/**
+ * Base exception class for package Doctrine\ODM\CouchDB\HTTP
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Kore Nordmann <kore@arbitracker.org>
+ */
+class HTTPException extends \Doctrine\CouchDB\CouchDBException
+{
+ /**
+ * @param string $ip
+ * @param integer $port
+ * @param string $errstr
+ * @param integer $errno
+ * @return \Doctrine\CouchDB\HTTP\HTTPException
+ */
+ public static function connectionFailure( $ip, $port, $errstr, $errno )
+ {
+ return new self( sprintf(
+ "Could not connect to server at %s:%d: '%d: %s'",
+ $ip,
+ $port,
+ $errno,
+ $errstr
+ ), $errno );
+ }
+
+ /**
+ * @param string $ip
+ * @param integer $port
+ * @param string $errstr
+ * @param integer $errno
+ * @return \Doctrine\CouchDB\HTTP\HTTPException
+ */
+ public static function readFailure( $ip, $port, $errstr, $errno )
+ {
+ return new static( sprintf(
+ "Could read from server at %s:%d: '%d: %s'",
+ $ip,
+ $port,
+ $errno,
+ $errstr
+ ), $errno );
+ }
+
+ /**
+ * @param string $path
+ * @param Response $response
+ * @return \Doctrine\CouchDB\HTTP\HTTPException
+ */
+ public static function fromResponse( $path, Response $response )
+ {
+ if (!isset($response->body['error'])) {
+ $response->body['error'] = '';
+ }
+
+ return new self(
+ "HTTP Error with status " . $response->status . " occoured while "
+ . "requesting " . $path . ". Error: " . $response->body['error']
+ . " " . $response->body['reason'],
+ $response->status );
+ }
+}
82 lib/Doctrine/CouchDB/HTTP/LoggingClient.php
@@ -0,0 +1,82 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+class LoggingClient implements Client
+{
+ /**
+ * @var Client
+ */
+ private $client;
+
+ /**
+ * Array of requests made to CouchDB with this client.
+ *
+ * Contains the following keys:
+ * - duration - Microseconds it took to execute and process the request
+ * - method (GET, POST, ..)
+ * - path - The requested url path on the server including parameters
+ * - request - The request body if its size is smaller than 10000 chars.
+ * - request_size - The size of the request body
+ * - response_status - The response HTTP status
+ * - response - The body of the response.
+ * - response_headers
+ *
+ * @var array
+ */
+ public $requests = array();
+
+ /**
+ * @var float
+ */
+ public $totalDuration = 0;
+
+ /**
+ * Construct new logging client wrapping the real client.
+ *
+ * @param Client $client
+ */
+ public function __construct(Client $client)
+ {
+ $this->client = $client;
+ }
+
+ public function request($method, $path, $data = null, $raw = false)
+ {
+ $start = microtime(true);
+
+ $response = $this->client->request($method, $path, $data, $raw);
+
+ $duration = microtime(true) - $start;
+ $this->requests[] = array(
+ 'duration' => $duration,
+ 'method' => $method,
+ 'path' => rawurldecode($path),
+ 'request' => $data,
+ 'request_size' => strlen($data),
+ 'response_status' => $response->status,
+ 'response' => $response->body,
+ 'response_headers' => $response->headers,
+ );
+ $this->totalDuration += $duration;
+
+ return $response;
+ }
+}
67 lib/Doctrine/CouchDB/HTTP/Response.php
@@ -0,0 +1,67 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+/**
+ * HTTP response
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Kore Nordmann <kore@arbitracker.org>
+ */
+class Response
+{
+ /**
+ * HTTP repsonse status
+ *
+ * @var int
+ */
+ public $status;
+
+ /**
+ * HTTP repsonse headers
+ *
+ * @var array
+ */
+ public $headers;
+
+ /**
+ * Decoded JSON response body
+ *
+ * @var array
+ */
+ public $body;
+
+ /**
+ * Construct response
+ *
+ * @param array $headers
+ * @param string $body
+ * @return void
+ */
+ public function __construct( $status, array $headers, $body, $raw = false )
+ {
+ $this->status = (int) $status;
+ $this->headers = $headers;
+ $this->body = $raw ? $body : json_decode( $body, true );
+ }
+}
+
260 lib/Doctrine/CouchDB/HTTP/SocketClient.php
@@ -0,0 +1,260 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+/**
+ * This class uses a custom HTTP client, which may have more bugs then the
+ * default PHP HTTP clients, but supports keep alive connections without any
+ * extension dependencies.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Kore Nordmann <kore@arbitracker.org>
+ */
+class SocketClient extends AbstractHTTPClient
+{
+ /**
+ * Connection pointer for connections, once keep alive is working on the
+ * CouchDb side.
+ *
+ * @var resource
+ */
+ protected $connection;
+
+ /**
+ * Check for server connection
+ *
+ * Checks if the connection already has been established, or tries to
+ * establish the connection, if not done yet.
+ *
+ * @return void
+ */
+ protected function checkConnection()
+ {
+ // If the connection could not be established, fsockopen sadly does not
+ // only return false (as documented), but also always issues a warning.
+ if ( ( $this->connection === null ) &&
+ ( ( $this->connection = @fsockopen( $this->options['ip'], $this->options['port'], $errno, $errstr ) ) === false ) )
+ {
+ // This is a bit hackisch...
+ $this->connection = null;
+ throw HTTPException::connectionFailure(
+ $this->options['ip'],
+ $this->options['port'],
+ $errstr,
+ $errno
+ );
+ }
+ }
+
+ /**
+ * Build a HTTP 1.1 request
+ *
+ * Build the HTTP 1.1 request headers from the gicven input.
+ *
+ * @param string $method
+ * @param string $path
+ * @param string $data
+ * @return string
+ */
+ protected function buildRequest( $method, $path, $data )
+ {
+ // Create basic request headers
+ $request = "$method $path HTTP/1.1\r\nHost: {$this->options['host']}\r\n";
+
+ // Add basic auth if set
+ if ( $this->options['username'] )
+ {
+ $request .= sprintf( "Authorization: Basic %s\r\n",
+ base64_encode( $this->options['username'] . ':' . $this->options['password'] )
+ );
+ }
+
+ // Set keep-alive header, which helps to keep to connection
+ // initilization costs low, especially when the database server is not
+ // available in the locale net.
+ $request .= "Connection: " . ( $this->options['keep-alive'] ? 'Keep-Alive' : 'Close' ) . "\r\n";
+
+ // Also add headers and request body if data should be sent to the
+ // server. Otherwise just add the closing mark for the header section
+ // of the request.
+ if ( $data !== null )
+ {
+ $request .= "Content-type: application/json\r\n";
+ $request .= "Content-Length: " . strlen( $data ) . "\r\n\r\n";
+ $request .= "$data";
+ }
+ else
+ {
+ $request .= "\r\n";
+ }
+
+ return $request;
+ }
+
+ /**
+ * Perform a request to the server and return the result
+ *
+ * Perform a request to the server and return the result converted into a
+ * Response object. If you do not expect a JSON structure, which
+ * could be converted in such a response object, set the forth parameter to
+ * true, and you get a response object retuerned, containing the raw body.
+ *
+ * @param string $method
+ * @param string $path
+ * @param string $data
+ * @param bool $raw
+ * @return Response
+ */
+ public function request( $method, $path, $data = null, $raw = false )
+ {
+ // Try establishing the connection to the server
+ $this->checkConnection();
+
+ // Send the build request to the server
+ if ( fwrite( $this->connection, $request = $this->buildRequest( $method, $path, $data ) ) === false )
+ {
+ // Reestablish which seems to have been aborted
+ //
+ // The recursion in this method might be problematic if the
+ // connection establishing mechanism does not correctly throw an
+ // exception on failure.
+ $this->connection = null;
+ return $this->request( $method, $path, $data, $raw );
+ }
+
+ // Read server response headers
+ $rawHeaders = '';
+ $headers = array(
+ 'connection' => ( $this->options['keep-alive'] ? 'Keep-Alive' : 'Close' ),
+ );
+
+ // Remove leading newlines, should not accur at all, actually.
+ while ( ( ( $line = fgets( $this->connection ) ) !== false ) &&
+ ( ( $lineContent = rtrim( $line ) ) === '' ) );
+
+ // Throw exception, if connection has been aborted by the server, and
+ // leave handling to the user for now.
+ if ( $line === false )
+ {
+ // Reestablish which seems to have been aborted
+ //
+ // The recursion in this method might be problematic if the
+ // connection establishing mechanism does not correctly throw an
+ // exception on failure.
+ //
+ // An aborted connection seems to happen here on long running
+ // requests, which cause a connection timeout at server side.
+ $this->connection = null;
+ return $this->request( $method, $path, $data, $raw );
+ }
+
+ do {
+ // Also store raw headers for later logging
+ $rawHeaders .= $lineContent . "\n";
+
+ // Extract header values
+ if ( preg_match( '(^HTTP/(?P<version>\d+\.\d+)\s+(?P<status>\d+))S', $lineContent, $match ) )
+ {
+ $headers['version'] = $match['version'];
+ $headers['status'] = (int) $match['status'];
+ }
+ else
+ {
+ list( $key, $value ) = explode( ':', $lineContent, 2 );
+ $headers[strtolower( $key )] = ltrim( $value );
+ }
+ } while ( ( ( $line = fgets( $this->connection ) ) !== false ) &&
+ ( ( $lineContent = rtrim( $line ) ) !== '' ) );
+
+ // Read response body
+ $body = '';
+ if ( !isset( $headers['transfer-encoding'] ) ||
+ ( $headers['transfer-encoding'] !== 'chunked' ) )
+ {
+ // HTTP 1.1 supports chunked transfer encoding, if the according
+ // header is not set, just read the specified amount of bytes.
+ $bytesToRead = (int) ( isset( $headers['content-length'] ) ? $headers['content-length'] : 0 );
+
+ // Read body only as specified by chunk sizes, everything else
+ // are just footnotes, which are not relevant for us.
+ while ( $bytesToRead > 0 )
+ {
+ $body .= $read = fgets( $this->connection, $bytesToRead + 1 );
+ $bytesToRead -= strlen( $read );
+ }
+ }
+ else
+ {
+ // When transfer-encoding=chunked has been specified in the
+ // response headers, read all chunks and sum them up to the body,
+ // until the server has finished. Ignore all additional HTTP
+ // options after that.
+ do {
+ $line = rtrim( fgets( $this->connection ) );
+
+ // Get bytes to read, with option appending comment
+ if ( preg_match( '(^([0-9a-f]+)(?:;.*)?$)', $line, $match ) )
+ {
+ $bytesToRead = hexdec( $match[1] );
+
+ // Read body only as specified by chunk sizes, everything else
+ // are just footnotes, which are not relevant for us.
+ $bytesLeft = $bytesToRead;
+ while ( $bytesLeft > 0 )
+ {
+ $body .= $read = fread( $this->connection, $bytesLeft + 2 );
+ $bytesLeft -= strlen( $read );
+ }
+ }
+ } while ( $bytesToRead > 0 );
+
+ // Chop off \r\n from the end.
+ $body = substr( $body, 0, -2 );
+ }
+
+ // Reset the connection if the server asks for it.
+ if ( $headers['connection'] !== 'Keep-Alive' )
+ {
+ fclose( $this->connection );
+ $this->connection = null;
+ }
+
+ // Handle some response state as special cases
+ switch ( $headers['status'] )
+ {
+ case 301:
+ case 302:
+ case 303:
+ case 307:
+ $path = parse_url( $headers['location'], PHP_URL_PATH );
+ return $this->request( $method, $path, $data, $raw );
+ }
+
+ // Create repsonse object from couch db response
+ if ( $headers['status'] >= 400 )
+ {
+ return new ErrorResponse( $headers['status'], $headers, $body );
+ }
+ return new Response( $headers['status'], $headers, $body, $raw );
+ }
+}
+
127 lib/Doctrine/CouchDB/HTTP/StreamClient.php
@@ -0,0 +1,127 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB\HTTP;
+
+/**
+ * Connection handler using PHPs stream wrappers.
+ *
+ * Requires PHP being compiled with --with-curlwrappers for now, since the PHPs
+ * own HTTP implementation is somehow b0rked.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.com
+ * @since 1.0
+ * @author Kore Nordmann <kore@arbitracker.org>
+ */
+class StreamClient extends AbstractHTTPClient
+{
+ /**
+ * Perform a request to the server and return the result
+ *
+ * Perform a request to the server and return the result converted into a
+ * Response object. If you do not expect a JSON structure, which
+ * could be converted in such a response object, set the forth parameter to
+ * true, and you get a response object retuerned, containing the raw body.
+ *
+ * @param string $method
+ * @param string $path
+ * @param string $data
+ * @return Response
+ */
+ public function request( $method, $path, $data = null, $raw = false )
+ {
+ $basicAuth = '';
+ if ( $this->options['username'] ) {
+ $basicAuth .= "{$this->options['username']}:{$this->options['password']}@";
+ }
+
+ // TODO SSL support?
+ $httpFilePointer = @fopen(
+ 'http://' . $basicAuth . $this->options['host'] . ':' . $this->options['port'] . $path,
+ 'r',
+ false,
+ stream_context_create(
+ array(
+ 'http' => array(
+ 'method' => $method,
+ 'content' => $data,
+ 'ignore_errors' => true,
+ 'max_redirects' => 0,
+ 'user_agent' => 'Doctrine CouchDB ODM $Revision$',
+ 'timeout' => $this->options['timeout'],
+ 'header' => 'Content-type: application/json',
+ ),
+ )
+ )
+ );
+
+ // Check if connection has been established successfully
+ if ( $httpFilePointer === false ) {
+ $error = error_get_last();
+ throw HTTPException::connectionFailure(
+ $this->options['ip'],
+ $this->options['port'],
+ $error['message'],
+ 0
+ );
+ }
+
+ // Read request body
+ $body = '';
+ while ( !feof( $httpFilePointer ) ) {
+ $body .= fgets( $httpFilePointer );
+ }
+
+ $metaData = stream_get_meta_data( $httpFilePointer );
+ // The structure of this array differs depending on PHP compiled with
+ // --enable-curlwrappers or not. Both cases are normally required.
+ $rawHeaders = isset( $metaData['wrapper_data']['headers'] )
+ ? $metaData['wrapper_data']['headers'] : $metaData['wrapper_data'];
+
+ $headers = array();
+ foreach ( $rawHeaders as $lineContent ) {
+ // Extract header values
+ if ( preg_match( '(^HTTP/(?P<version>\d+\.\d+)\s+(?P<status>\d+))S', $lineContent, $match ) ) {
+ $headers['version'] = $match['version'];
+ $headers['status'] = (int) $match['status'];
+ } else {
+ list( $key, $value ) = explode( ':', $lineContent, 2 );
+ $headers[strtolower( $key )] = ltrim( $value );
+ }
+ }
+
+ if ( empty($headers['status']) ) {
+ throw HTTPException::readFailure(
+ $this->options['ip'],
+ $this->options['port'],
+ 'Received an empty response or not status code',
+ 0
+ );
+ }
+
+ // Create repsonse object from couch db response
+ if ( $headers['status'] >= 400 )
+ {
+ return new ErrorResponse( $headers['status'], $headers, $body );
+ }
+ return new Response( $headers['status'], $headers, $body, $raw );
+ }
+}
+
23 lib/Doctrine/CouchDB/JsonDecodeException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Doctrine\CouchDB;
+
+class JsonDecodeException extends \Exception
+{
+ static public function fromLastJsonError()
+ {
+ $lastError = \json_last_error();
+ switch ($lastError) {
+ case \JSON_ERROR_DEPTH:
+ return new self("The maximum stack depth has been exceeded");
+ case \JSON_ERROR_STATE_MISMATCH:
+ return new self("Invalid or malformed JSON");
+ case \JSON_ERROR_CTRL_CHAR:
+ return new self("Control character error, possibly incorrectly encoded");
+ case \JSON_ERROR_SYNTAX:
+ return new self("Syntax error");
+ default:
+ return new self("An unknownerror occured with code: " . $lastError);
+ }
+ }
+}
44 lib/Doctrine/CouchDB/Tools/Console/Command/CompactDatabaseCommand.php
@@ -0,0 +1,44 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\CouchDB\Tools\Console\Command;
+
+use Symfony\Component\Console\Input\InputArgument,
+ Symfony\Component\Console\Input\InputOption,
+ Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Console\Command\Command;
+
+class CompactDatabaseCommand extends Command
+{
+ protected function configure()
+ {
+ $this->setName('couchdb:maintenance:compact-database')
+ ->setDescription('Compact the database');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $couchClient = $this->getHelper('couchdb')->getCouchDBClient();
+ /* @var $couchClient \Doctrine\CouchDB\CouchDBClient */
+
+ $data = $couchClient->compactDatabase();
+ $output->writeln("Database compact started.");
+ }
+}
47 lib/Doctrine/CouchDB/Tools/Console/Command/CompactViewCommand.php
@@ -0,0 +1,47 @@
+<?php
+/*
+ * THIS SOFTWARE IS