diff --git a/composer.json b/composer.json index 99ebde011cd..1ac23c60f71 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "phenx/php-svg-lib": "0.1", "dompdf/dompdf": "0.7.0", "doctrine/common": "2.5.0", - "doctrine/couchdb": "1.0.*@dev" + "doctrine/couchdb": "1.0-beta2" }, "require-dev": { diff --git a/composer.lock b/composer.lock index f99d50d8974..4b68faeabd6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "1bfa073bfb896cff8008bf3c6bee0afb", - "content-hash": "b37af8fb1408d0175063c6e3b8023a71", + "hash": "b29cf79e9ba3f7956a7013c31185010a", + "content-hash": "1f4768c7d449f6dd6e846ce01a1f2be5", "packages": [ { "name": "adodb/adodb-php", @@ -340,23 +340,21 @@ }, { "name": "doctrine/couchdb", - "version": "dev-master", + "version": "1.0.0-beta2", "source": { "type": "git", "url": "https://github.com/doctrine/couchdb-client.git", - "reference": "911c01716730af0fc0b5074a0740fddce3156ac0" + "reference": "5e8d808eba8d790d178e5558c182c648a12ec8e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/couchdb-client/zipball/911c01716730af0fc0b5074a0740fddce3156ac0", - "reference": "911c01716730af0fc0b5074a0740fddce3156ac0", + "url": "https://api.github.com/repos/doctrine/couchdb-client/zipball/5e8d808eba8d790d178e5558c182c648a12ec8e4", + "reference": "5e8d808eba8d790d178e5558c182c648a12ec8e4", "shasum": "" }, "require": { - "php": ">=5.4" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" + "doctrine/common": "@stable", + "php": ">=5.3.2" }, "type": "library", "extra": { @@ -371,7 +369,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "LGPL" ], "authors": [ { @@ -389,7 +387,7 @@ "couchdb", "persistence" ], - "time": "2016-08-06 11:26:41" + "time": "2015-07-06 16:08:10" }, { "name": "doctrine/inflector", @@ -1161,7 +1159,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "doctrine/couchdb": 20 + "doctrine/couchdb": 10 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 669616e976d..4f2eebad55a 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -706,61 +706,6 @@ "parser" ] }, - { - "name": "doctrine/couchdb", - "version": "dev-master", - "version_normalized": "9999999-dev", - "source": { - "type": "git", - "url": "https://github.com/doctrine/couchdb-client.git", - "reference": "911c01716730af0fc0b5074a0740fddce3156ac0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/couchdb-client/zipball/911c01716730af0fc0b5074a0740fddce3156ac0", - "reference": "911c01716730af0fc0b5074a0740fddce3156ac0", - "shasum": "" - }, - "require": { - "php": ">=5.4" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "time": "2016-08-06 11:26:41", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "installation-source": "source", - "autoload": { - "psr-0": { - "Doctrine\\CouchDB": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Lukas Kahwe Smith", - "email": "smith@pooteeweet.org" - } - ], - "description": "CouchDB Client", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "couchdb", - "persistence" - ] - }, { "name": "zendframework/zendframework", "version": "2.4.9", @@ -1089,5 +1034,58 @@ "persistence", "spl" ] + }, + { + "name": "doctrine/couchdb", + "version": "1.0.0-beta2", + "version_normalized": "1.0.0.0-beta2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/couchdb-client.git", + "reference": "5e8d808eba8d790d178e5558c182c648a12ec8e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/couchdb-client/zipball/5e8d808eba8d790d178e5558c182c648a12ec8e4", + "reference": "5e8d808eba8d790d178e5558c182c648a12ec8e4", + "shasum": "" + }, + "require": { + "doctrine/common": "@stable", + "php": ">=5.3.2" + }, + "time": "2015-07-06 16:08:10", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\CouchDB": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Lukas Kahwe Smith", + "email": "smith@pooteeweet.org" + } + ], + "description": "CouchDB Client", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "couchdb", + "persistence" + ] } ] diff --git a/vendor/doctrine/couchdb b/vendor/doctrine/couchdb deleted file mode 160000 index 911c0171673..00000000000 --- a/vendor/doctrine/couchdb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 911c01716730af0fc0b5074a0740fddce3156ac0 diff --git a/vendor/doctrine/couchdb/.travis.yml b/vendor/doctrine/couchdb/.travis.yml new file mode 100644 index 00000000000..d0db04860e1 --- /dev/null +++ b/vendor/doctrine/couchdb/.travis.yml @@ -0,0 +1,14 @@ +language: php + +services: + - couchdb + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + +before_script: + - curl -X PUT localhost:5984/doctrine_test_database + - composer install diff --git a/vendor/doctrine/couchdb/LICENSE b/vendor/doctrine/couchdb/LICENSE new file mode 100644 index 00000000000..4edf3e9c125 --- /dev/null +++ b/vendor/doctrine/couchdb/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2006-2012 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/doctrine/couchdb/README.md b/vendor/doctrine/couchdb/README.md new file mode 100644 index 00000000000..b60bc117929 --- /dev/null +++ b/vendor/doctrine/couchdb/README.md @@ -0,0 +1,40 @@ +# Doctrine CouchDB Client + +[![Build Status](https://travis-ci.org/doctrine/couchdb-client.png?branch=master)](https://travis-ci.org/doctrine/couchdb-client) + +Simple API that wraps around CouchDBs HTTP API. + +## Features + +* Create, Delete Databases +* Create, Update, Delete Documents +* Bulk API for Creating/Updating Documents +* Find Documents by ID +* Generate UUIDs +* Query `_all_docs` view +* Query Changes Feed +* Compaction Info and Triggering APIs +* Replication API +* Symfony Console Commands + +## Installation + +With Composer: + + { + "require": { + "doctrine/couchdb": "@dev" + } + } + +## Usage + +```php +postDocument(array('foo' => 'bar')); +$client->putDocument(array('foo' => 'baz'), $id, $rev); + +$doc = $client->findDocument($id); +``` diff --git a/vendor/doctrine/couchdb/composer.json b/vendor/doctrine/couchdb/composer.json new file mode 100644 index 00000000000..8607f051df8 --- /dev/null +++ b/vendor/doctrine/couchdb/composer.json @@ -0,0 +1,26 @@ +{ + "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": "@stable" + }, + "autoload": { + "psr-0": { + "Doctrine\\CouchDB": "lib/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/doctrine/couchdb/composer.lock b/vendor/doctrine/couchdb/composer.lock new file mode 100644 index 00000000000..d1c80d75e8f --- /dev/null +++ b/vendor/doctrine/couchdb/composer.lock @@ -0,0 +1,93 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "ecfe6be48dffe89867613aae861ac79f", + "packages": [ + { + "name": "doctrine/common", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common", + "reference": "2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/common/zipball/2.3.0", + "reference": "2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common": "lib/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2012-09-19 22:55:18" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": { + "doctrine/common": 0 + }, + "platform": { + "php": ">=5.3.2" + }, + "platform-dev": [ + + ] +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Attachment.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Attachment.php new file mode 100644 index 00000000000..db520e17d97 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Attachment.php @@ -0,0 +1,265 @@ +. + */ + + +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 + */ +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); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/CouchDBClient.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/CouchDBClient.php new file mode 100644 index 00000000000..8d57d470da8 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/CouchDBClient.php @@ -0,0 +1,568 @@ +. + */ + +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 + * @author Lukas Kahwe Smith + */ +class CouchDBClient +{ + /** string \ufff0 */ + const COLLATION_END = "\xEF\xBF\xB0"; + + /** + * Name of the CouchDB database + * + * @string + */ + protected $databaseName; + + /** + * The underlying HTTP Connection of the used DocumentManager. + * + * @var 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 + * @throws \InvalidArgumentException + */ + 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'], implode(", ", 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 static($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 Client + */ + public function getHttpClient() + { + return $this->httpClient; + } + + public function getDatabase() + { + return $this->databaseName; + } + + /** + * Let CouchDB generate an array of UUIDs. + * + * @param int $count + * @return array + * @throws CouchDBException + */ + 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 HTTP\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 + * @param null $limit + * @param null $offset + * @return HTTP\Response + */ + 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 HTTP\Response + */ + 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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + public function getChanges(array $params = array()) + { + $path = '/' . $this->databaseName . '/_changes'; + + $method = ((!isset($params['doc_ids']) || $params['doc_ids'] == null) ? "GET" : "POST"); + $response = ''; + + if ($method == "GET") { + + foreach ($params as $key => $value) { + if (isset($params[$key]) === true && is_bool($value) === true) { + $params[$key] = ($value) ? 'true': 'false'; + } + } + if (count($params) > 0) { + $query = http_build_query($params); + $path = $path.'?'.$query; + } + $response = $this->httpClient->request('GET', $path); + + } else { + $path .= '?filter=_doc_ids'; + $response = $this->httpClient->request('POST', $path, json_encode($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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + 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 = null) + { + 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; + + $documentPath = '/' . $this->databaseName . '/' . $data['_id']; + $response = $this->httpClient->request( 'GET', $documentPath ); + + 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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + 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 + * @throws HTTPException + */ + 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; + } + + /** + * GET /_active_tasks + * + * @return array + * @throws HTTPException + */ + public function getActiveTasks() + { + $response = $this->httpClient->request('GET', '/_active_tasks'); + if ($response->status != 200) { + throw HTTPException::fromResponse('/_active_tasks', $response); + } + return $response->body; + } + + /** + * Get revision difference. + * + * @param array $data + * @return array + * @throws HTTPException + */ + public function getRevisionDifference($data) + { + $path = '/' . $this->databaseName . '/_revs_diff'; + $response = $this->httpClient->request('POST', $path, json_encode($data)); + if ($response->status != 200) { + throw HTTPException::fromResponse($path, $response); + } + return $response->body; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/CouchDBException.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/CouchDBException.php new file mode 100644 index 00000000000..13cd4f79e9d --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/CouchDBException.php @@ -0,0 +1,65 @@ +. + */ + +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 + */ +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."); + } +} + diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/AbstractHTTPClient.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/AbstractHTTPClient.php new file mode 100644 index 00000000000..45a5273cf51 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/AbstractHTTPClient.php @@ -0,0 +1,95 @@ + + */ +abstract class AbstractHTTPClient implements Client +{ + /** + * CouchDB connection options + * + * @var array + */ + protected $options = array( + 'host' => 'localhost', + 'port' => 5984, + 'ip' => '127.0.0.1', + 'ssl' => false, + '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 + * @param bool $ssl + * @return \Doctrine\CouchDB\HTTP\AbstractHTTPClient + */ + public function __construct( $host = 'localhost', $port = 5984, $username = null, $password = null, $ip = null , $ssl = false ) + { + $this->options['host'] = (string) $host; + $this->options['port'] = (int) $port; + $this->options['ssl'] = $ssl; + $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 + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setOption( $option, $value ) + { + switch ( $option ) { + case 'keep-alive': + case 'ssl': + $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." ); + } + } +} + diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/Client.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/Client.php new file mode 100644 index 00000000000..61b2cd39cec --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/Client.php @@ -0,0 +1,42 @@ +. + */ + +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 returned, containing the raw body. + * Optional HTTP request headers can be passed in an array using the fifth + * parameter. + * + * @param string $method + * @param string $path + * @param string $data + * @param bool $raw + * @param array $headers + * @return Response + */ + function request( $method, $path, $data = null, $raw = false, array $headers = array() ); +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/ErrorResponse.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/ErrorResponse.php new file mode 100644 index 00000000000..3e50c074435 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/ErrorResponse.php @@ -0,0 +1,19 @@ + + */ +class ErrorResponse extends Response +{ +} + diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/HTTPException.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/HTTPException.php new file mode 100644 index 00000000000..f1d12636975 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/HTTPException.php @@ -0,0 +1,80 @@ + + */ +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 ) + { + $response = self::fixCloudantBulkCustomError($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 ); + } + + private static function fixCloudantBulkCustomError($response) + { + if (isset($response->body[0]['error']) && isset($response->body[0]['reason'])) { + $response->body['error'] = $response->body[0]['error']; + $response->body['reason'] = $response->body[0]['reason']; + } + + return $response; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/LoggingClient.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/LoggingClient.php new file mode 100644 index 00000000000..5021c8700d7 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/LoggingClient.php @@ -0,0 +1,82 @@ +. + */ + +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, array $headers = array()) + { + $start = microtime(true); + + $response = $this->client->request($method, $path, $data, $raw, $headers); + + $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; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/Response.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/Response.php new file mode 100644 index 00000000000..87bb4b802e7 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/Response.php @@ -0,0 +1,69 @@ +. + */ + +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 + */ +class Response +{ + /** + * HTTP response status + * + * @var int + */ + public $status; + + /** + * HTTP response headers + * + * @var array + */ + public $headers; + + /** + * Decoded JSON response body + * + * @var array + */ + public $body; + + /** + * Construct response + * + * @param $status + * @param array $headers + * @param string $body + * @param bool $raw + * @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 ); + } +} + diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/SocketClient.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/SocketClient.php new file mode 100644 index 00000000000..00e4ebc3c6f --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/SocketClient.php @@ -0,0 +1,294 @@ +. + */ + +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 + */ +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 + * @throws HTTPException + */ + protected function checkConnection() + { + // Setting Connection scheme according ssl support + if ($this->options['ssl']) { + if (!extension_loaded('openssl')) { + // no openssl extension loaded. + // This is a bit hackisch... + $this->connection = null; + + throw HTTPException::connectionFailure( + $this->options['ip'], + $this->options['port'], + "ssl activated without openssl extension loaded", + 0 + ); + } + + $host = 'ssl://' . $this->options['host']; + + } else { + $host = $this->options['ip']; + } + + // 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($host, $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 given input. + * + * @param string $method + * @param string $path + * @param string $data + * @param array $headers + * @return string + */ + protected function buildRequest( $method, $path, $data, $headers) + { + // 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 + // initialization 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"; + + if (!isset($headers['Content-Type'])) { + $headers['Content-Type'] = 'application/json'; + } + foreach ($headers as $key => $value) { + if (is_bool($value) === true) { + $value = ($value) ? 'true': 'false'; + } + $request .= $key . ": ". $value . "\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-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 returned, containing the raw body. + * + * @param string $method + * @param string $path + * @param string $data + * @param bool $raw + * @param array $headers + * @return Response + */ + public function request( $method, $path, $data = null, $raw = false, array $headers = array() ) + { + // 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, $headers ) ) === 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 occur 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\d+\.\d+)\s+(?P\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 response object from couch db response + if ( $headers['status'] >= 400 ) + { + return new ErrorResponse( $headers['status'], $headers, $body ); + } + return new Response( $headers['status'], $headers, $body, $raw ); + } +} + diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/StreamClient.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/StreamClient.php new file mode 100644 index 00000000000..2ba44d2080a --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/HTTP/StreamClient.php @@ -0,0 +1,140 @@ +. + */ + +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 + */ +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 returned, containing the raw body. + * + * @param string $method + * @param string $path + * @param string $data + * @param bool $raw + * @param array $headers + * @return Response + * @throws HTTPException + */ + public function request( $method, $path, $data = null, $raw = false, array $headers = array()) + { + $basicAuth = ''; + if ( $this->options['username'] ) { + $basicAuth .= "{$this->options['username']}:{$this->options['password']}@"; + } + if (!isset($headers['Content-Type'])) { + $headers['Content-Type'] = 'application/json'; + } + $stringHeader = ''; + if ($headers != null) { + foreach ($headers as $key => $val) { + $stringHeader .= $key . ": " . $val . "\r\n"; + } + } + + // 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' => $stringHeader, + ), + ) + ) + ); + + // 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\d+\.\d+)\s+(?P\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 response object from couch db response + if ( $headers['status'] >= 400 ) + { + return new ErrorResponse( $headers['status'], $headers, $body ); + } + return new Response( $headers['status'], $headers, $body, $raw ); + } + +} + diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/JsonDecodeException.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/JsonDecodeException.php new file mode 100644 index 00000000000..1f73d1660dc --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/JsonDecodeException.php @@ -0,0 +1,23 @@ +. + */ + +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."); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/CompactViewCommand.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/CompactViewCommand.php new file mode 100644 index 00000000000..2f5a9755e26 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/CompactViewCommand.php @@ -0,0 +1,47 @@ +. + */ + +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 CompactViewCommand extends Command +{ + protected function configure() + { + $this->setName('couchdb:maintenance:compact-view') + ->setDescription('Compat the given view') + ->setDefinition(array( + new InputArgument('designdoc', InputArgument::REQUIRED, 'Design document name', null), + )); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $couchClient = $this->getHelper('couchdb')->getCouchDBClient(); + /* @var $couchClient \Doctrine\CouchDB\CouchDBClient */ + + $data = $couchClient->compactView($input->getArgument('designdoc')); + $output->writeln("View compact started."); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/MigrationCommand.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/MigrationCommand.php new file mode 100644 index 00000000000..d7c64f69d67 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/MigrationCommand.php @@ -0,0 +1,51 @@ +. + */ + +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 MigrationCommand extends Command +{ + protected function configure() + { + $this->setName('couchdb:migrate') + ->setDescription('Execute a migration in CouchDB.') + ->setDefinition(array( + new InputArgument('class', InputArgument::REQUIRED, 'Migration class name', null), + )); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $className = $input->getArgument('class'); + if (!class_exists($className) || !in_array('Doctrine\CouchDB\Tools\Migrations\AbstractMigration', class_parents($className))) { + throw new \InvalidArgumentException("class passed to command has to extend 'Doctrine\CouchDB\Tools\Migrations\AbstractMigration'"); + } + $migration = new $className($this->getHelper('couchdb')->getCouchDBClient()); + $migration->execute(); + + $output->writeln("Migration was successfully executed!"); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationCancelCommand.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationCancelCommand.php new file mode 100644 index 00000000000..7728f37aebf --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationCancelCommand.php @@ -0,0 +1,62 @@ +. + */ + +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 ReplicationCancelCommand extends Command +{ + protected function configure() + { + $this->setName('couchdb:replication:cancel') + ->setDescription('Cancel replication from a given source to target.') + ->setDefinition(array( + new InputArgument('source', InputArgument::REQUIRED, 'Source Database', null), + new InputArgument('target', InputArgument::REQUIRED, 'Target Database', null), + new InputOption('continuous', 'c', InputOption::VALUE_NONE, 'Enable continuous replication', null), + ))->setHelp(<<getHelper('couchdb')->getCouchDBClient(); + /* @var $couchClient \Doctrine\CouchDB\CouchDBClient */ + $data = $couchClient->replicate( + $input->getArgument('source'), + $input->getArgument('target'), + true, + $input->getOption('continuous') ? true : false + ); + + $output->writeln("Replication canceled."); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationStartCommand.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationStartCommand.php new file mode 100644 index 00000000000..e26561f063f --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ReplicationStartCommand.php @@ -0,0 +1,67 @@ +. + */ + +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 ReplicationStartCommand extends Command +{ + protected function configure() + { + $this->setName('couchdb:replication:start') + ->setDescription('Start replication from a given source to target.') + ->setDefinition(array( + new InputArgument('source', InputArgument::REQUIRED, 'Source Database', null), + new InputArgument('target', InputArgument::REQUIRED, 'Target Database', null), + new InputOption('continuous', 'c', InputOption::VALUE_NONE, 'Enable continuous replication', null), + new InputOption('proxy', 'p', InputOption::VALUE_REQUIRED, 'Proxy server to replicate through', null), + new InputOption('id', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Ids for named replication', null), + new InputOption('filter', 'f', InputOption::VALUE_REQUIRED, 'Replication-Filter Document', null), + ))->setHelp(<<getHelper('couchdb')->getCouchDBClient(); + /* @var $couchClient \Doctrine\CouchDB\CouchDBClient */ + $data = $couchClient->replicate( + $input->getArgument('source'), + $input->getArgument('target'), null, + $input->getOption('continuous') ? true : false, + $input->getOption('filter') ?: null, + $input->getOption('id') ?: null, + $input->getOption('proxy') ?: null + ); + + $output->writeln("Replication started."); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ViewCleanupCommand.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ViewCleanupCommand.php new file mode 100644 index 00000000000..4683b23e45e --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Command/ViewCleanupCommand.php @@ -0,0 +1,44 @@ +. + */ + +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 ViewCleanupCommand extends Command +{ + protected function configure() + { + $this->setName('couchdb:maintenance:view-cleanup') + ->setDescription('Cleanup deleted views'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $couchClient = $this->getHelper('couchdb')->getCouchDBClient(); + /* @var $couchClient \Doctrine\CouchDB\CouchDBClient */ + + $data = $couchClient->viewCleanup(); + $output->writeln("View cleanup started."); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Helper/CouchDBHelper.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Helper/CouchDBHelper.php new file mode 100644 index 00000000000..67689ed4651 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Console/Helper/CouchDBHelper.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\CouchDB\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Doctrine\CouchDB\CouchDBClient; +use Doctrine\ODM\CouchDB\DocumentManager; + +/** + * Doctrine CLI Connection Helper. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class CouchDBHelper extends Helper +{ + protected $couchDBClient; + + /** + * @var DocumentManager + */ + protected $dm; + + /** + * Constructor + * + * @param CouchDBClient $couchDBClient + * @param DocumentManager $dm + */ + public function __construct(CouchDBClient $couchDBClient = null, DocumentManager $dm = null) + { + if (!$couchDBClient && $dm) { + $couchDBClient = $dm->getCouchDBClient(); + } + + $this->couchDBClient = $couchDBClient; + $this->dm = $dm; + } + + /** + * Retrieves Doctrine ODM CouchDB Manager + * + * @return \Doctrine\ODM\CouchDB\DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } + + /** + * @return CouchDBClient + */ + public function getCouchDBClient() + { + return $this->couchDBClient; + } + + /** + * @see Helper + */ + public function getName() + { + return 'couchdb'; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Migrations/AbstractMigration.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Migrations/AbstractMigration.php new file mode 100644 index 00000000000..9e2fcca1ab1 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Tools/Migrations/AbstractMigration.php @@ -0,0 +1,72 @@ +. + */ +namespace Doctrine\CouchDB\Tools\Migrations; + +use Doctrine\CouchDB\CouchDBClient; + +/** + * Migration base class + */ +abstract class AbstractMigration +{ + private $client; + + public function __construct(CouchDBClient $client) + { + $this->client = $client; + } + + /** + * Execute migration by iterating over all documents in batches of 100. + * + * @return void + * @throws \RuntimeException + */ + public function execute() + { + $response = $this->client->allDocs(100); + $lastKey = null; + + do { + if ($response->status !== 200) { + throw new \RuntimeException("Error while migrating at offset " . $offset); + } + + $bulkUpdater = $this->client->createBulkUpdater(); + foreach ($response->body['rows'] AS $row) { + $doc = $this->migrate($row['doc']); + if ($doc) { + $bulkUpdater->updateDocument($doc); + } + $lastKey = $row['key']; + } + + $bulkUpdater->execute(); + $response = $this->client->allDocs(100, $lastKey); + } while (count($response->body['rows']) > 1); + } + + /** + * Return an array of to migrate to document data or null if this document should not be migrated. + * + * @param array $docData + * @return array|bool|null $docData + */ + abstract protected function migrate(array $docData); +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Utils/BulkUpdater.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Utils/BulkUpdater.php new file mode 100644 index 00000000000..45e9d11ada5 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Utils/BulkUpdater.php @@ -0,0 +1,90 @@ +. + */ + + +namespace Doctrine\CouchDB\Utils; + +use Doctrine\CouchDB\HTTP\Client; + +/** + * Bulk updater class + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class BulkUpdater +{ + private $data = array('docs' => array()); + + private $requestHeaders = array(); + + private $httpClient; + + private $databaseName; + + public function __construct(Client $httpClient, $databaseName) + { + $this->httpClient = $httpClient; + $this->databaseName = $databaseName; + } + + public function setAllOrNothing($allOrNothing) + { + $this->data['all_or_nothing'] = (bool)$allOrNothing; + } + + public function updateDocument($data) + { + $this->data['docs'][] = $data; + } + + public function updateDocuments(array $docs) + { + foreach ($docs as $doc) { + $this->data['docs'][] = (is_array($doc) ? $doc : json_decode($doc, true)); + } + } + + public function deleteDocument($id, $rev) + { + $this->data['docs'][] = array('_id' => $id, '_rev' => $rev, '_deleted' => true); + } + + public function setNewEdits($newEdits) + { + $this->data["new_edits"] = (bool)$newEdits; + } + + public function setFullCommitHeader($commit) + { + $this->requestHeaders['X-Couch-Full-Commit'] = (bool)$commit; + } + + public function execute() + { + return $this->httpClient->request('POST', $this->getPath(), json_encode($this->data), false, $this->requestHeaders); + } + + public function getPath() + { + return '/' . $this->databaseName . '/_bulk_docs'; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Version.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Version.php new file mode 100644 index 00000000000..19066371b6d --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/Version.php @@ -0,0 +1,25 @@ +. + */ + +namespace Doctrine\CouchDB; + +class Version +{ + const VERSION = '1.0.0ALPHA2'; +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/AbstractQuery.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/AbstractQuery.php new file mode 100644 index 00000000000..4bc9b42c29b --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/AbstractQuery.php @@ -0,0 +1,132 @@ +client = $client; + $this->databaseName = $databaseName; + $this->designDocumentName = $designDocName; + $this->viewName = $viewName; + $this->doc = $doc; + } + + /** + * @param string $key + * @return mixed + */ + public function getParameter($key) + { + if (isset($this->params[$key])) { + return $this->params[$key]; + } + return null; + } + + abstract protected function getHttpQuery(); + + /** + * Query the view with the current params. + * + * @return Result + */ + public function execute() + { + return $this->createResult($this->doExecute()); + } + + protected function doExecute() + { + $path = $this->getHttpQuery(); + $response = $this->client->request("GET", $path); + + if ( $response instanceof ErrorResponse ) { + // Create view, if it does not exist yet + $this->createDesignDocument(); + $response = $this->client->request( "GET", $path ); + } + + if ($response->status >= 400) { + throw HTTPException::fromResponse($path, $response); + } + return $response; + } + + /** + * @param $response + * @return Result + */ + abstract protected function createResult($response); + + /** + * Create non existing view + * + * @return void + * @throws \Doctrine\CouchDB\JsonDecodeException + * @throws \Exception + */ + public function createDesignDocument() + { + if (!$this->doc) { + throw new \Exception("No DesignDocument Class is connected to this view query, cannot create the design document with its corresponding view automatically!"); + } + + $data = $this->doc->getData(); + if ($data === null) { + throw \Doctrine\CouchDB\JsonDecodeException::fromLastJsonError(); + } + $data['_id'] = '_design/' . $this->designDocumentName; + + $response = $this->client->request( + "PUT", + sprintf( + "/%s/_design/%s", + $this->databaseName, + $this->designDocumentName + ), + json_encode($data) + ); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/DesignDocument.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/DesignDocument.php new file mode 100644 index 00000000000..ad7ab095663 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/DesignDocument.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\CouchDB\View; + +/** + * Abstract Design Document + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +interface DesignDocument +{ + /** + * Get design doc code + * + * Return the view (or general design doc) code, which should be + * committed to the database, which should be structured like: + * + * + * array( + * "views" => array( + * "name" => array( + * "map" => "code", + * ["reduce" => "code"], + * ), + * ... + * ) + * ) + * + */ + public function getData(); +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/FolderDesignDocument.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/FolderDesignDocument.php new file mode 100644 index 00000000000..c57086fae12 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/FolderDesignDocument.php @@ -0,0 +1,62 @@ +folderPath = realpath($folderPath); + } + + public function getData() + { + if ($this->data === null) { + $rdi = new \RecursiveDirectoryIterator($this->folderPath, \FilesystemIterator::CURRENT_AS_FILEINFO); + $ri = new \RecursiveIteratorIterator($rdi, \RecursiveIteratorIterator::LEAVES_ONLY); + + $this->data = array(); + foreach ($ri AS $path) { + $fileData = $this->getFileData($path); + if ($fileData !== null) { + $parts = explode(DIRECTORY_SEPARATOR, ltrim(str_replace($this->folderPath, '', $fileData["key"]), DIRECTORY_SEPARATOR)); + + if (count($parts) == 3) { + $this->data[$parts[0]][$parts[1]][$parts[2]] = $fileData["data"]; + } else if (count($parts) == 2) { + $this->data[$parts[0]][$parts[1]] = $fileData["data"]; + } else if (count($parts) == 1) { + $this->data[$parts[0]] = $fileData["data"]; + } + } + } + + $this->data['language'] = 'javascript'; + } + + return $this->data; + } + + private function getFileData($path) + { + $result = null; + if (substr($path, -3) === ".js") { + $result = array("key" => str_replace(".js", "", $path), + "data"=> file_get_contents($path)); + } else if (substr($path, -5) === ".json") { + $result = array("key" => str_replace(".json", "", $path), + "data"=> json_decode(file_get_contents($path), true)); + } + return $result; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/LuceneQuery.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/LuceneQuery.php new file mode 100644 index 00000000000..b0bf3919f9d --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/LuceneQuery.php @@ -0,0 +1,122 @@ +handlerName = $handlerName; + } + + protected function getHttpQuery() + { + return sprintf( + "/%s/%s/_design/%s/%s?%s", + $this->databaseName, + $this->handlerName, + $this->designDocumentName, + $this->viewName, + http_build_query( $this->params ) + ); + } + + public function setAnalyzer($analyzer) + { + $this->params['analyzer'] = $analyzer; + return $this; + } + + public function getAnalyzer() + { + return (isset($this->params['analyzer'])) ? $this->params['analyzer'] : null; + } + + /** + * Automatically fetch and include the document which emitted each view entry + * + * @param bool $flag + * @return Query + */ + public function setIncludeDocs($flag) + { + $this->params['include_docs'] = $flag; + return $this; + } + + public function getIncludeDocs() + { + return (isset($this->params['include_docs'])) ? $this->params['include_docs'] : null; + } + + public function setLimit($limit) + { + $this->params['limit'] = $limit; + return $this; + } + + public function getLimit() + { + return (isset($this->params['limit'])) ? $this->params['limit'] : null; + } + + public function setQuery($query) + { + $this->params['q'] = $query; + return $this; + } + + public function getQuery() + { + return isset($this->params['q']) ? $this->params['q'] : null; + } + + public function setSkip($skip) + { + $this->params['skip'] = $skip; + return $this; + } + + public function setSort($sort) + { + $this->params['sort'] = $sort; + return $this; + } + + public function setStale($bool) + { + if ($bool) { + $this->params['stale'] = 'ok'; + } else { + unset($this->params['stale']); + } + return $this; + } + + /** + * @param \Doctrine\CouchDB\HTTP\Response $response + * @return LuceneResult + */ + protected function createResult($response) + { + return new LuceneResult($response->body); + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/LuceneResult.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/LuceneResult.php new file mode 100644 index 00000000000..bb4a5d65bb2 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/LuceneResult.php @@ -0,0 +1,46 @@ +result['etag']; + } + + public function getFetchDuration() + { + return $this->result['fetch_duration']; + } + + public function getLimit() + { + return $this->result['limit']; + } + + public function getExecutedQuery() + { + return $this->result['q']; + } + + public function getRows() + { + return $this->result['rows']; + } + + public function getSearchDuration() + { + return $this->result['search_duration']; + } + + public function getSkip() + { + return $this->result['skip']; + } + + public function getTotalRows() + { + return $this->result['total_rows']; + } +} \ No newline at end of file diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/Query.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/Query.php new file mode 100644 index 00000000000..959b5833460 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/Query.php @@ -0,0 +1,230 @@ +. + */ + +namespace Doctrine\CouchDB\View; + +/** + * Query class + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class Query extends AbstractQuery +{ + protected function createResult($response) + { + return new Result($response->body); + } + + protected function getHttpQuery() + { + $arguments = array(); + foreach ($this->params as $key => $value) { + if ($key === 'stale') { + $arguments[$key] = $value; + } else { + $arguments[$key] = json_encode($value); + } + } + + return sprintf( + "/%s/_design/%s/_view/%s?%s", + $this->databaseName, + $this->designDocumentName, + $this->viewName, + http_build_query($arguments) + ); + } + + /** + * Find key in view. + * + * @param string|array $val + * @return Query + */ + public function setKey($val) + { + $this->params['key'] = $val; + return $this; + } + + /** + * Find keys in the view + * + * @param array $values + * @return Query + */ + public function setKeys(array $values) + { + $this->params['keys'] = $values; + return $this; + } + + /** + * Set starting key to query view for. + * + * @param string $val + * @return Query + */ + public function setStartKey($val) + { + $this->params['startkey'] = $val; + return $this; + } + + /** + * Set ending key to query view for. + * + * @param string $val + * @return Query + */ + public function setEndKey($val) + { + $this->params['endkey'] = $val; + return $this; + } + + /** + * Document id to start with + * + * @param string $val + * @return Query + */ + public function setStartKeyDocId($val) + { + $this->params['startkey_docid'] = $val; + return $this; + } + + /** + * Last document id to include in the output + * + * @param string $val + * @return Query + */ + public function setEndKeyDocId($val) + { + $this->params['endkey_docid'] = $val; + return $this; + } + + /** + * Limit the number of documents in the output + * + * @param int $val + * @return Query + */ + public function setLimit($val) + { + $this->params['limit'] = $val; + return $this; + } + + /** + * Skip n number of documents + * + * @param int $val + * @return Query + */ + public function setSkip($val) + { + $this->params['skip'] = $val; + return $this; + } + + /** + * If stale=ok is set CouchDB will not refresh the view even if it is stalled. + * + * @param bool $flag + * @return Query + */ + public function setStale($flag) + { + $this->params['stale'] = $flag; + return $this; + } + + /** + * reverse the output + * + * @param bool $flag + * @return Query + */ + public function setDescending($flag) + { + $this->params['descending'] = $flag; + return $this; + } + + /** + * The group option controls whether the reduce function reduces to a set of distinct keys or to a single result row. + * + * @param bool $flag + * @return Query + */ + public function setGroup($flag) + { + $this->params['group'] = $flag; + return $this; + } + + public function setGroupLevel($level) + { + $this->params['group_level'] = $level; + return $this; + } + + /** + * Use the reduce function of the view. It defaults to true, if a reduce function is defined and to false otherwise. + * + * @param bool $flag + * @return Query + */ + public function setReduce($flag) + { + $this->params['reduce'] = $flag; + return $this; + } + + /** + * Controls whether the endkey is included in the result. It defaults to true. + * + * @param bool $flag + * @return Query + */ + public function setInclusiveEnd($flag) + { + $this->params['inclusive_end'] = $flag; + return $this; + } + + /** + * Automatically fetch and include the document which emitted each view entry + * + * @param bool $flag + * @return Query + */ + public function setIncludeDocs($flag) + { + $this->params['include_docs'] = $flag; + return $this; + } +} diff --git a/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/Result.php b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/Result.php new file mode 100644 index 00000000000..6398a179f77 --- /dev/null +++ b/vendor/doctrine/couchdb/lib/Doctrine/CouchDB/View/Result.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\CouchDB\View; + +class Result implements \IteratorAggregate, \Countable, \ArrayAccess +{ + protected $result; + + public function __construct($result) + { + $this->result = $result; + } + + public function getTotalRows() + { + return $this->result['total_rows']; + } + + public function getIterator() + { + return new \ArrayIterator($this->result['rows']); + } + + public function count() + { + return count($this->result['rows']); + } + + public function offsetExists($offset) + { + return isset($this->result['rows'][$offset]); + } + + public function offsetGet($offset) + { + return $this->result['rows'][$offset]; + } + + public function offsetSet($offset, $value) + { + throw new \BadMethodCallException("Result is immutable and cannot be changed."); + } + + public function offsetUnset($offset) + { + throw new \BadMethodCallException("Result is immutable and cannot be changed."); + } + + public function toArray() + { + return $this->result['rows']; + } +} diff --git a/vendor/doctrine/couchdb/phpunit.xml.dist b/vendor/doctrine/couchdb/phpunit.xml.dist new file mode 100644 index 00000000000..2175f336574 --- /dev/null +++ b/vendor/doctrine/couchdb/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + + + + + + + ./tests/Doctrine/Tests/CouchDB + + +