Phalcon Framework 2.1.0 RC 1

triagens\ArangoDb\ConnectException: cannot connect to endpoint 'tcp://45.55.53.44:8529/': Connection refused

/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/HttpHelper.php (260)
#0triagens\ArangoDb\HttpHelper::createConnection(Object(triagens\ArangoDb\ConnectionOptions))
/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Connection.php (347)
<?php
 
/**
 * ArangoDB PHP client: connection
 *
 * @package   triagens\ArangoDb
 * @author    Jan Steemann
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */
 
namespace triagens\ArangoDb;
 
/**
 * Provides access to the ArangoDB server
 *
 * As all access is done using HTTP, we do not need to establish a
 * persistent connection and keep its state.<br>
 * Instead, connections are established on the fly for each request
 * and are destroyed afterwards.<br>
 * <br>
 *
 * @package   triagens\ArangoDb
 * @since     0.2
 */
class Connection
{
    /**
     * Api Version
     *
     * @var string
     */
    public static $_apiVersion = 20200;
 
    /**
     * Connection options
     *
     * @var array
     */
    private $_options;
 
    /**
     * Connection handle, used in case of keep-alive
     *
     * @var resource
     */
    private $_handle;
 
    /**
     * Flag if keep-alive connections are used
     *
     * @var bool
     */
    private $_useKeepAlive;
 
    /**
     * Batches Array
     *
     * @var array
     */
    private $_batches = array();
 
    /**
     * $_activeBatch object
     *
     * @var array
     */
    private $_activeBatch = null;
 
    /**
     * $_captureBatch boolean
     *
     * @var boolean
     */
    private $_captureBatch = false;
 
    /**
     * $_batchRequest boolean
     *
     * @var boolean
     */
    private $_batchRequest = false;
 
    /**
     * custom queue name (leave empty if no custom queue is required)
     *
     * @var string
     */
    private $_customQueue = null;
 
    /**
     * $_database string
     *
     * @var string
     */
    private $_database = '';
 
    /**
     * Set up the connection object, validate the options provided
     *
     * @throws Exception
     *
     * @param array $options - initial connection options
     *
     * @return Connection
     */
    public function __construct(array $options)
    {
        $this->_options      = new ConnectionOptions($options);
        $this->_useKeepAlive = ($this->_options[ConnectionOptions::OPTION_CONNECTION] === 'Keep-Alive');
        $this->setDatabase($this->_options[ConnectionOptions::OPTION_DATABASE]);
    }
 
    /**
     * Close existing connection handle if a keep-alive connection was used
     *
     * @return void
     */
    public function __destruct()
    {
        if ($this->_useKeepAlive && is_resource($this->_handle)) {
            @fclose($this->_handle);
        }
    }
 
    /**
     * Get an option set for the connection
     *
     * @throws ClientException
     *
     * @param string $name - name of option
     *
     * @return mixed
     */
    public function getOption($name)
    {
        assert(is_string($name));
 
        return $this->_options[$name];
    }
 
    /**
     * Set an option set for the connection
     *
     * @throws ClientException
     *
     * @param string $name - name of option
     * @param string $value - value of option
     */
    public function setOption($name, $value) {
        if ($name === ConnectionOptions::OPTION_ENDPOINT ||
            $name === ConnectionOptions::OPTION_HOST ||
            $name === ConnectionOptions::OPTION_PORT) {
            throw new ClientException('Must not set option ' . $value . ' after connection is created.');
        }
 
        $this->_options[$name] = $value;
 
        // special handling for several options
        if ($name === ConnectionOptions::OPTION_TIMEOUT) {
            // set the timeout option: patch the stream of an existing connection
            if (is_resource($this->_handle)) {
                stream_set_timeout($this->_handle, $value); 
            }
        }
        else if ($name === ConnectionOptions::OPTION_CONNECTION) {
          // set keep-alive flag
          $this->_useKeepAlive = (strtolower($value) === 'keep-alive');
        }
        else if ($name === ConnectionOptions::OPTION_DATABASE) {
          // set database
          $this->setDatabase($value);
        }
    }
 
 
    /**
     * Enables a custom queue name for all actions of the connection
     *
     * @param string $queueName - queue name
     * @param number $count - number of requests the custom queue will be used for
     * @internal this method is currently experimental. whether or not it will 
     *           become part of the official API needs decision
     */
 
    public function enableCustomQueue($queueName, $count = null) 
    {
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] = $queueName;
 
        if ($count !== null) {
            if (! is_numeric($count) || $count <= 0) {
                throw new ClientException('Invalid value for count value of custom queues');
            }
            $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT] = $count;
        }
    }
 
    /**
     * Disable usage of custom queue for all actions of the connection
     *
     * @internal this method is currently experimental. whether or not it will 
     *           become part of the official API needs decision
     */
    public function disableCustomQueue() 
    {
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] = null;
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT] = null;
    }
 
 
    /**
     * Issue an HTTP GET request
     *
     * @throws Exception
     *
     * @param string $url - GET URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function get($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_GET, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP POST request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - POST URL
     * @param string $data - body to post
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function post($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_POST, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP PUT request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - PUT URL
     * @param string $data - body to post
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function put($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_PUT, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP Head request with the data provided
     *
     * @throws Exception
     *
     * @param string $url - PUT URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function head($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_HEAD, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP PATCH request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - PATCH URL
     * @param string $data - patch body
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function patch($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_PATCH, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP DELETE request with the data provided
     *
     * @throws Exception
     *
     * @param string $url - DELETE URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function delete($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_DELETE, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
 
    /**
     * Get a connection handle
     *
     * If keep-alive connections are used, the handle will be stored and re-used
     *
     * @throws ClientException
     * @return resource - connection handle
     */
    private function getHandle()
    {
        if ($this->_useKeepAlive && $this->_handle && is_resource($this->_handle)) {
            // keep-alive and handle was created already
            $handle = $this->_handle;
 
            // check if connection is still valid
            if (!feof($handle)) {
                // connection still valid
                return $handle;
            }
 
            // close handle
            @fclose($this->_handle);
            $this->_handle = 0;
 
            if (!$this->_options[ConnectionOptions::OPTION_RECONNECT]) {
                // if reconnect option not set, this is the end
                throw new ClientException('Server has closed the connection already.');
            }
        }
 
        // no keep-alive or no handle available yet or a reconnect
        $handle = HttpHelper::createConnection($this->_options);
 
        if ($this->_useKeepAlive && is_resource($handle)) {
            $this->_handle = $handle;
        }
 
        return $handle;
    }
 
    /**
     * Parse the response return the body values as an assoc array
     *
     * @throws Exception
     *
     * @param HttpResponse $response - the response as supplied by the server
     *
     * @return HttpResponse
     */
    public function parseResponse(HttpResponse $response)
    {
        $httpCode = $response->getHttpCode();
 
        if ($httpCode < 200 || $httpCode >= 400) {
            // failure on server
 
            $body = $response->getBody();
            if ($body != '') {
                // check if we can find details in the response body
                $details = json_decode($body, true);
                if (is_array($details) && isset($details["errorMessage"])) {
                    // yes, we got details
                    $exception = new ServerException($details["errorMessage"], $details["code"]);
                    $exception->setDetails($details);
                    throw $exception;
                }
            }
 
            // no details found, throw normal exception
            throw new ServerException($response->getResult(), $httpCode);
        }
 
        return $response;
    }
 
    /**
     * Execute an HTTP request and return the results
     *
     * This function will throw if no connection to the server can be established or if
     * there is a problem during data exchange with the server.
     *
     * will restore it.
     *
     * @throws Exception
     *
     * @param string $method        - HTTP request method
     * @param string $url           - HTTP URL
     * @param string $data          - data to post in body
     * @param array  $customHeaders - any array containing header elements
     *
     * @return HttpResponse
     */
    private function executeRequest($method, $url, $data, array $customHeaders = array())
    {
        HttpHelper::validateMethod($method);
        $database = $this->getDatabase();
        if ($database === '') {
            $url = '/_db/' . '_system' . $url;
        } else {
            $url = '/_db/' . urlencode($database) . $url;
        }
 
        // check if a custom queue should be used
        if (! isset($customHeaders[ConnectionOptions::OPTION_CUSTOM_QUEUE]) &&
            $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] !== null) {
 
            $customHeaders[HttpHelper::QUEUE_HEADER] = $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE]; 
 
            // check if a counter is set for the custom queue
            $count = $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT];
            if ($count !== null) {
                // yes, now decrease the counter
 
                if ($count === 1) {
                    $this->disableCustomQueue();
                }
                else {
                    $this->_options->offsetSet(ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT, $count - 1);
                }
            }
        }
 
        // create request data
        if ($this->_batchRequest === false) {
 
            if ($this->_captureBatch === true) {
                $this->_options->offsetSet(ConnectionOptions::OPTION_BATCHPART, true);
                $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
                $this->_options->offsetSet(ConnectionOptions::OPTION_BATCHPART, false);
            } else {
                $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
            }
 
            if ($this->_captureBatch === true) {
                $batchPart = $this->doBatch($method, $request);
                if (!is_null($batchPart)) {
                    return $batchPart;
                }
            }
        } else {
            $this->_batchRequest = false;
 
            $this->_options->offsetSet(ConnectionOptions::OPTION_BATCH, true);
 
            $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
            $this->_options->offsetSet(ConnectionOptions::OPTION_BATCH, false);
        }
 
 
        $traceFunc = $this->_options[ConnectionOptions::OPTION_TRACE];
        if ($traceFunc) {
            // call tracer func
            if ($this->_options[ConnectionOptions::OPTION_ENHANCED_TRACE]) {
                list($header) = HttpHelper::parseHttpMessage($request, $url, $method);
                $headers = HttpHelper::parseHeaders($header);
                $traceFunc(new TraceRequest($headers[2], $method, $url, $data));
            } else {
                $traceFunc('send', $request);
            }
        }
 
 
        // open the socket. note: this might throw if the connection cannot be established
        $handle = $this->getHandle();
 
        if ($handle) {
            // send data and get response back
 
            if ($traceFunc) {
                // only issue syscall if we need it
                $startTime = microtime(true);
            }
 
            $result = HttpHelper::transfer($handle, $request);
 
            if ($traceFunc) {
                // only issue syscall if we need it
                $timeTaken = microtime(true) - $startTime;
            }
 
            $status = socket_get_status($handle);
            if ($status['timed_out']) {
                throw new ClientException('Got a timeout while waiting for the server\'s response', 408);
            }
 
            if (!$this->_useKeepAlive) {
                // must close the connection
                fclose($handle);
            }
 
            $response = new HttpResponse($result, $url, $method);
 
            if ($traceFunc) {
                // call tracer func
                if ($this->_options[ConnectionOptions::OPTION_ENHANCED_TRACE]) {
                    $traceFunc(
                        new TraceResponse($response->getHeaders(), $response->getHttpCode(), $response->getBody(),
                            $timeTaken)
                    );
                } else {
                    $traceFunc('receive', $result);
                }
            }
 
            return $response;
        }
 
        throw new ClientException('Whoops, this should never happen');
    }
 
    /**
     * Get the client version (alias for getClientVersion)
     *
     * @return string
     */
    public static function getVersion()
    {
        return self::getClientVersion();
    }
 
 
    /**
     * Get the client version
     *
     * @return string
     */
    public static function getClientVersion()
    {
        return self::$_apiVersion;
    }
 
    /**
     * Stop capturing commands
     *
     * @return Batch - Returns the active batch object
     */
    public function stopCaptureBatch()
    {
        $this->_captureBatch = false;
 
        return $this->getActiveBatch();
    }
 
 
    /**
     * returns the active batch
     *
     * @return Batch active batch
     */
    public function getActiveBatch()
    {
        return $this->_activeBatch;
    }
 
    /**
     * Sets the active Batch for this connection
     *
     * @param Batch $batch - Sets the given batch as active
     *
     * @return Batch active batch
     */
    public function setActiveBatch($batch)
    {
        $this->_activeBatch = $batch;
 
        return $this->_activeBatch;
    }
 
 
    /**
     * Sets the batch capture state (true, if capturing)
     *
     * @param boolean $state true to turn on capture batch mode, false to turn it off
     */
    public function setCaptureBatch($state)
    {
        $this->_captureBatch = $state;
    }
 
 
    /**
     * Sets connection into Batch-request mode. This is needed for some operations to act differently when in this mode.
     *
     * @param boolean $state sets the connection state to batch request, meaning it is currently doing a batch request.
     */
    public function setBatchRequest($state)
    {
        $this->_batchRequest = $state;
    }
 
 
    /**
     * Returns true if this connection is in Batch-Capture mode
     *
     * @return bool
     *
     * returns the active batch
     */
    public function isInBatchCaptureMode()
    {
        return $this->_captureBatch;
    }
 
 
    /**
     * returns the active batch
     *
     */
    public function getBatches()
    {
        return $this->_batches;
    }
 
 
    /**
     * This is a helper function to executeRequest that captures requests if we're in batch mode
     *
     * @param mixed  $method  - The method of the request (GET, POST...)
     *
     * @param string $request - The request to process
     *
     * This checks if we're in batch mode and returns a placeholder object,
     * since we need to return some object that is expected by the caller.
     * if we're not in batch mode it doesn't return anything, and
     *
     * @return mixed Batchpart or null if not in batch capturing mode
     */
    private function doBatch($method, $request)
    {
        $batchPart = null;
        if ($this->_captureBatch === true) {
 
            /** @var $batch Batch */
            $batch = $this->getActiveBatch();
 
            $batchPart = $batch->append($method, $request);
        }
 
        # do batch processing
        return $batchPart;
    }
 
 
    /**
     * This function checks that the encoding of a string is utf.
     * It only checks for printable characters.
     *
     *
     * @param array $string the data to check
     *
     * @return boolean true if string is UTF-8, false if not
     */
    public static function detect_utf($string)
    {
        if (preg_match("//u", $string)) {
            return true;
        } else {
            return false;
        }
    }
 
 
    /**
     * This function checks that the encoding of the keys and
     * values of the array are utf-8, recursively.
     * It will raise an exception if it encounters wrong encoded strings.
     *
     * @param array $data the data to check
     *
     * @throws ClientException
     */
    public static function check_encoding($data)
    {
        foreach ($data as $key => $value) {
            if (!is_array($value)) {
                // check if the multibyte library function is installed and use it.
                if (function_exists('mb_detect_encoding')) {
                    // check with mb library
                    if (mb_detect_encoding($key, 'UTF-8', true) === false) {
                        throw new ClientException("Only UTF-8 encoded keys allowed. Wrong encoding in key string: " . $key);
                    }
                    if (mb_detect_encoding($value, 'UTF-8', true) === false) {
                        throw new ClientException("Only UTF-8 encoded values allowed. Wrong encoding in value string: " . $value);
                    }
                } else {
                    // fallback to preg_match checking
                    if (self::detect_utf($key) == false) {
                        throw new ClientException("Only UTF-8 encoded keys allowed. Wrong encoding in key string: " . $key);
                    }
                    if (self::detect_utf($value) == false) {
                        throw new ClientException("Only UTF-8 encoded values allowed. Wrong encoding in value string: " . $value);
                    }
                }
            } else {
                self::check_encoding($value);
            }
        }
    }
 
 
    /**
     * This is a json_encode() wrapper that also checks if the data is utf-8 conform.
     * internally it calls the check_encoding() method. If that method does not throw
     * an Exception, this method will happily return the json_encoded data.
     *
     * @param mixed $data    the data to encode
     * @param mixed $options the options for the json_encode() call
     *
     * @return string the result of the json_encode
     */
    public function json_encode_wrapper($data, $options = null)
    {
        if ($this->_options[ConnectionOptions::OPTION_CHECK_UTF8_CONFORM] === true) {
            self::check_encoding($data);
        }
        if (empty($data)) {
            $response = json_encode($data, $options | JSON_FORCE_OBJECT);
        } else {
            $response = json_encode($data, $options);
        }
 
        return $response;
    }
 
 
    /**
     * Set the database to use with this connection
     *
     * Sets the database to use with this connection, for example: 'my_database'<br>
     * Further calls to the database will be addressed to the given database.
     *
     * @param string $database the database to use
     */
    public function setDatabase($database)
    {
        $this->_options[ConnectionOptions::OPTION_DATABASE] = $database;
        $this->_database = $database;
    }
 
    /**
     * Get the database that is currently used with this connection
     *
     * Get the database to use with this connection, for example: 'my_database'
     *
     * @return string
     */
    public function getDatabase()
    {
        return $this->_database;
    }
}
#1triagens\ArangoDb\Connection->getHandle()
/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Connection.php (479)
<?php
 
/**
 * ArangoDB PHP client: connection
 *
 * @package   triagens\ArangoDb
 * @author    Jan Steemann
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */
 
namespace triagens\ArangoDb;
 
/**
 * Provides access to the ArangoDB server
 *
 * As all access is done using HTTP, we do not need to establish a
 * persistent connection and keep its state.<br>
 * Instead, connections are established on the fly for each request
 * and are destroyed afterwards.<br>
 * <br>
 *
 * @package   triagens\ArangoDb
 * @since     0.2
 */
class Connection
{
    /**
     * Api Version
     *
     * @var string
     */
    public static $_apiVersion = 20200;
 
    /**
     * Connection options
     *
     * @var array
     */
    private $_options;
 
    /**
     * Connection handle, used in case of keep-alive
     *
     * @var resource
     */
    private $_handle;
 
    /**
     * Flag if keep-alive connections are used
     *
     * @var bool
     */
    private $_useKeepAlive;
 
    /**
     * Batches Array
     *
     * @var array
     */
    private $_batches = array();
 
    /**
     * $_activeBatch object
     *
     * @var array
     */
    private $_activeBatch = null;
 
    /**
     * $_captureBatch boolean
     *
     * @var boolean
     */
    private $_captureBatch = false;
 
    /**
     * $_batchRequest boolean
     *
     * @var boolean
     */
    private $_batchRequest = false;
 
    /**
     * custom queue name (leave empty if no custom queue is required)
     *
     * @var string
     */
    private $_customQueue = null;
 
    /**
     * $_database string
     *
     * @var string
     */
    private $_database = '';
 
    /**
     * Set up the connection object, validate the options provided
     *
     * @throws Exception
     *
     * @param array $options - initial connection options
     *
     * @return Connection
     */
    public function __construct(array $options)
    {
        $this->_options      = new ConnectionOptions($options);
        $this->_useKeepAlive = ($this->_options[ConnectionOptions::OPTION_CONNECTION] === 'Keep-Alive');
        $this->setDatabase($this->_options[ConnectionOptions::OPTION_DATABASE]);
    }
 
    /**
     * Close existing connection handle if a keep-alive connection was used
     *
     * @return void
     */
    public function __destruct()
    {
        if ($this->_useKeepAlive && is_resource($this->_handle)) {
            @fclose($this->_handle);
        }
    }
 
    /**
     * Get an option set for the connection
     *
     * @throws ClientException
     *
     * @param string $name - name of option
     *
     * @return mixed
     */
    public function getOption($name)
    {
        assert(is_string($name));
 
        return $this->_options[$name];
    }
 
    /**
     * Set an option set for the connection
     *
     * @throws ClientException
     *
     * @param string $name - name of option
     * @param string $value - value of option
     */
    public function setOption($name, $value) {
        if ($name === ConnectionOptions::OPTION_ENDPOINT ||
            $name === ConnectionOptions::OPTION_HOST ||
            $name === ConnectionOptions::OPTION_PORT) {
            throw new ClientException('Must not set option ' . $value . ' after connection is created.');
        }
 
        $this->_options[$name] = $value;
 
        // special handling for several options
        if ($name === ConnectionOptions::OPTION_TIMEOUT) {
            // set the timeout option: patch the stream of an existing connection
            if (is_resource($this->_handle)) {
                stream_set_timeout($this->_handle, $value); 
            }
        }
        else if ($name === ConnectionOptions::OPTION_CONNECTION) {
          // set keep-alive flag
          $this->_useKeepAlive = (strtolower($value) === 'keep-alive');
        }
        else if ($name === ConnectionOptions::OPTION_DATABASE) {
          // set database
          $this->setDatabase($value);
        }
    }
 
 
    /**
     * Enables a custom queue name for all actions of the connection
     *
     * @param string $queueName - queue name
     * @param number $count - number of requests the custom queue will be used for
     * @internal this method is currently experimental. whether or not it will 
     *           become part of the official API needs decision
     */
 
    public function enableCustomQueue($queueName, $count = null) 
    {
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] = $queueName;
 
        if ($count !== null) {
            if (! is_numeric($count) || $count <= 0) {
                throw new ClientException('Invalid value for count value of custom queues');
            }
            $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT] = $count;
        }
    }
 
    /**
     * Disable usage of custom queue for all actions of the connection
     *
     * @internal this method is currently experimental. whether or not it will 
     *           become part of the official API needs decision
     */
    public function disableCustomQueue() 
    {
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] = null;
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT] = null;
    }
 
 
    /**
     * Issue an HTTP GET request
     *
     * @throws Exception
     *
     * @param string $url - GET URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function get($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_GET, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP POST request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - POST URL
     * @param string $data - body to post
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function post($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_POST, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP PUT request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - PUT URL
     * @param string $data - body to post
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function put($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_PUT, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP Head request with the data provided
     *
     * @throws Exception
     *
     * @param string $url - PUT URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function head($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_HEAD, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP PATCH request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - PATCH URL
     * @param string $data - patch body
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function patch($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_PATCH, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP DELETE request with the data provided
     *
     * @throws Exception
     *
     * @param string $url - DELETE URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function delete($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_DELETE, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
 
    /**
     * Get a connection handle
     *
     * If keep-alive connections are used, the handle will be stored and re-used
     *
     * @throws ClientException
     * @return resource - connection handle
     */
    private function getHandle()
    {
        if ($this->_useKeepAlive && $this->_handle && is_resource($this->_handle)) {
            // keep-alive and handle was created already
            $handle = $this->_handle;
 
            // check if connection is still valid
            if (!feof($handle)) {
                // connection still valid
                return $handle;
            }
 
            // close handle
            @fclose($this->_handle);
            $this->_handle = 0;
 
            if (!$this->_options[ConnectionOptions::OPTION_RECONNECT]) {
                // if reconnect option not set, this is the end
                throw new ClientException('Server has closed the connection already.');
            }
        }
 
        // no keep-alive or no handle available yet or a reconnect
        $handle = HttpHelper::createConnection($this->_options);
 
        if ($this->_useKeepAlive && is_resource($handle)) {
            $this->_handle = $handle;
        }
 
        return $handle;
    }
 
    /**
     * Parse the response return the body values as an assoc array
     *
     * @throws Exception
     *
     * @param HttpResponse $response - the response as supplied by the server
     *
     * @return HttpResponse
     */
    public function parseResponse(HttpResponse $response)
    {
        $httpCode = $response->getHttpCode();
 
        if ($httpCode < 200 || $httpCode >= 400) {
            // failure on server
 
            $body = $response->getBody();
            if ($body != '') {
                // check if we can find details in the response body
                $details = json_decode($body, true);
                if (is_array($details) && isset($details["errorMessage"])) {
                    // yes, we got details
                    $exception = new ServerException($details["errorMessage"], $details["code"]);
                    $exception->setDetails($details);
                    throw $exception;
                }
            }
 
            // no details found, throw normal exception
            throw new ServerException($response->getResult(), $httpCode);
        }
 
        return $response;
    }
 
    /**
     * Execute an HTTP request and return the results
     *
     * This function will throw if no connection to the server can be established or if
     * there is a problem during data exchange with the server.
     *
     * will restore it.
     *
     * @throws Exception
     *
     * @param string $method        - HTTP request method
     * @param string $url           - HTTP URL
     * @param string $data          - data to post in body
     * @param array  $customHeaders - any array containing header elements
     *
     * @return HttpResponse
     */
    private function executeRequest($method, $url, $data, array $customHeaders = array())
    {
        HttpHelper::validateMethod($method);
        $database = $this->getDatabase();
        if ($database === '') {
            $url = '/_db/' . '_system' . $url;
        } else {
            $url = '/_db/' . urlencode($database) . $url;
        }
 
        // check if a custom queue should be used
        if (! isset($customHeaders[ConnectionOptions::OPTION_CUSTOM_QUEUE]) &&
            $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] !== null) {
 
            $customHeaders[HttpHelper::QUEUE_HEADER] = $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE]; 
 
            // check if a counter is set for the custom queue
            $count = $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT];
            if ($count !== null) {
                // yes, now decrease the counter
 
                if ($count === 1) {
                    $this->disableCustomQueue();
                }
                else {
                    $this->_options->offsetSet(ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT, $count - 1);
                }
            }
        }
 
        // create request data
        if ($this->_batchRequest === false) {
 
            if ($this->_captureBatch === true) {
                $this->_options->offsetSet(ConnectionOptions::OPTION_BATCHPART, true);
                $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
                $this->_options->offsetSet(ConnectionOptions::OPTION_BATCHPART, false);
            } else {
                $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
            }
 
            if ($this->_captureBatch === true) {
                $batchPart = $this->doBatch($method, $request);
                if (!is_null($batchPart)) {
                    return $batchPart;
                }
            }
        } else {
            $this->_batchRequest = false;
 
            $this->_options->offsetSet(ConnectionOptions::OPTION_BATCH, true);
 
            $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
            $this->_options->offsetSet(ConnectionOptions::OPTION_BATCH, false);
        }
 
 
        $traceFunc = $this->_options[ConnectionOptions::OPTION_TRACE];
        if ($traceFunc) {
            // call tracer func
            if ($this->_options[ConnectionOptions::OPTION_ENHANCED_TRACE]) {
                list($header) = HttpHelper::parseHttpMessage($request, $url, $method);
                $headers = HttpHelper::parseHeaders($header);
                $traceFunc(new TraceRequest($headers[2], $method, $url, $data));
            } else {
                $traceFunc('send', $request);
            }
        }
 
 
        // open the socket. note: this might throw if the connection cannot be established
        $handle = $this->getHandle();
 
        if ($handle) {
            // send data and get response back
 
            if ($traceFunc) {
                // only issue syscall if we need it
                $startTime = microtime(true);
            }
 
            $result = HttpHelper::transfer($handle, $request);
 
            if ($traceFunc) {
                // only issue syscall if we need it
                $timeTaken = microtime(true) - $startTime;
            }
 
            $status = socket_get_status($handle);
            if ($status['timed_out']) {
                throw new ClientException('Got a timeout while waiting for the server\'s response', 408);
            }
 
            if (!$this->_useKeepAlive) {
                // must close the connection
                fclose($handle);
            }
 
            $response = new HttpResponse($result, $url, $method);
 
            if ($traceFunc) {
                // call tracer func
                if ($this->_options[ConnectionOptions::OPTION_ENHANCED_TRACE]) {
                    $traceFunc(
                        new TraceResponse($response->getHeaders(), $response->getHttpCode(), $response->getBody(),
                            $timeTaken)
                    );
                } else {
                    $traceFunc('receive', $result);
                }
            }
 
            return $response;
        }
 
        throw new ClientException('Whoops, this should never happen');
    }
 
    /**
     * Get the client version (alias for getClientVersion)
     *
     * @return string
     */
    public static function getVersion()
    {
        return self::getClientVersion();
    }
 
 
    /**
     * Get the client version
     *
     * @return string
     */
    public static function getClientVersion()
    {
        return self::$_apiVersion;
    }
 
    /**
     * Stop capturing commands
     *
     * @return Batch - Returns the active batch object
     */
    public function stopCaptureBatch()
    {
        $this->_captureBatch = false;
 
        return $this->getActiveBatch();
    }
 
 
    /**
     * returns the active batch
     *
     * @return Batch active batch
     */
    public function getActiveBatch()
    {
        return $this->_activeBatch;
    }
 
    /**
     * Sets the active Batch for this connection
     *
     * @param Batch $batch - Sets the given batch as active
     *
     * @return Batch active batch
     */
    public function setActiveBatch($batch)
    {
        $this->_activeBatch = $batch;
 
        return $this->_activeBatch;
    }
 
 
    /**
     * Sets the batch capture state (true, if capturing)
     *
     * @param boolean $state true to turn on capture batch mode, false to turn it off
     */
    public function setCaptureBatch($state)
    {
        $this->_captureBatch = $state;
    }
 
 
    /**
     * Sets connection into Batch-request mode. This is needed for some operations to act differently when in this mode.
     *
     * @param boolean $state sets the connection state to batch request, meaning it is currently doing a batch request.
     */
    public function setBatchRequest($state)
    {
        $this->_batchRequest = $state;
    }
 
 
    /**
     * Returns true if this connection is in Batch-Capture mode
     *
     * @return bool
     *
     * returns the active batch
     */
    public function isInBatchCaptureMode()
    {
        return $this->_captureBatch;
    }
 
 
    /**
     * returns the active batch
     *
     */
    public function getBatches()
    {
        return $this->_batches;
    }
 
 
    /**
     * This is a helper function to executeRequest that captures requests if we're in batch mode
     *
     * @param mixed  $method  - The method of the request (GET, POST...)
     *
     * @param string $request - The request to process
     *
     * This checks if we're in batch mode and returns a placeholder object,
     * since we need to return some object that is expected by the caller.
     * if we're not in batch mode it doesn't return anything, and
     *
     * @return mixed Batchpart or null if not in batch capturing mode
     */
    private function doBatch($method, $request)
    {
        $batchPart = null;
        if ($this->_captureBatch === true) {
 
            /** @var $batch Batch */
            $batch = $this->getActiveBatch();
 
            $batchPart = $batch->append($method, $request);
        }
 
        # do batch processing
        return $batchPart;
    }
 
 
    /**
     * This function checks that the encoding of a string is utf.
     * It only checks for printable characters.
     *
     *
     * @param array $string the data to check
     *
     * @return boolean true if string is UTF-8, false if not
     */
    public static function detect_utf($string)
    {
        if (preg_match("//u", $string)) {
            return true;
        } else {
            return false;
        }
    }
 
 
    /**
     * This function checks that the encoding of the keys and
     * values of the array are utf-8, recursively.
     * It will raise an exception if it encounters wrong encoded strings.
     *
     * @param array $data the data to check
     *
     * @throws ClientException
     */
    public static function check_encoding($data)
    {
        foreach ($data as $key => $value) {
            if (!is_array($value)) {
                // check if the multibyte library function is installed and use it.
                if (function_exists('mb_detect_encoding')) {
                    // check with mb library
                    if (mb_detect_encoding($key, 'UTF-8', true) === false) {
                        throw new ClientException("Only UTF-8 encoded keys allowed. Wrong encoding in key string: " . $key);
                    }
                    if (mb_detect_encoding($value, 'UTF-8', true) === false) {
                        throw new ClientException("Only UTF-8 encoded values allowed. Wrong encoding in value string: " . $value);
                    }
                } else {
                    // fallback to preg_match checking
                    if (self::detect_utf($key) == false) {
                        throw new ClientException("Only UTF-8 encoded keys allowed. Wrong encoding in key string: " . $key);
                    }
                    if (self::detect_utf($value) == false) {
                        throw new ClientException("Only UTF-8 encoded values allowed. Wrong encoding in value string: " . $value);
                    }
                }
            } else {
                self::check_encoding($value);
            }
        }
    }
 
 
    /**
     * This is a json_encode() wrapper that also checks if the data is utf-8 conform.
     * internally it calls the check_encoding() method. If that method does not throw
     * an Exception, this method will happily return the json_encoded data.
     *
     * @param mixed $data    the data to encode
     * @param mixed $options the options for the json_encode() call
     *
     * @return string the result of the json_encode
     */
    public function json_encode_wrapper($data, $options = null)
    {
        if ($this->_options[ConnectionOptions::OPTION_CHECK_UTF8_CONFORM] === true) {
            self::check_encoding($data);
        }
        if (empty($data)) {
            $response = json_encode($data, $options | JSON_FORCE_OBJECT);
        } else {
            $response = json_encode($data, $options);
        }
 
        return $response;
    }
 
 
    /**
     * Set the database to use with this connection
     *
     * Sets the database to use with this connection, for example: 'my_database'<br>
     * Further calls to the database will be addressed to the given database.
     *
     * @param string $database the database to use
     */
    public function setDatabase($database)
    {
        $this->_options[ConnectionOptions::OPTION_DATABASE] = $database;
        $this->_database = $database;
    }
 
    /**
     * Get the database that is currently used with this connection
     *
     * Get the database to use with this connection, for example: 'my_database'
     *
     * @return string
     */
    public function getDatabase()
    {
        return $this->_database;
    }
}
#2triagens\ArangoDb\Connection->executeRequest(GET, /_api/document/shorten/RxQ4Pq17, , Array())
/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Connection.php (222)
<?php
 
/**
 * ArangoDB PHP client: connection
 *
 * @package   triagens\ArangoDb
 * @author    Jan Steemann
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */
 
namespace triagens\ArangoDb;
 
/**
 * Provides access to the ArangoDB server
 *
 * As all access is done using HTTP, we do not need to establish a
 * persistent connection and keep its state.<br>
 * Instead, connections are established on the fly for each request
 * and are destroyed afterwards.<br>
 * <br>
 *
 * @package   triagens\ArangoDb
 * @since     0.2
 */
class Connection
{
    /**
     * Api Version
     *
     * @var string
     */
    public static $_apiVersion = 20200;
 
    /**
     * Connection options
     *
     * @var array
     */
    private $_options;
 
    /**
     * Connection handle, used in case of keep-alive
     *
     * @var resource
     */
    private $_handle;
 
    /**
     * Flag if keep-alive connections are used
     *
     * @var bool
     */
    private $_useKeepAlive;
 
    /**
     * Batches Array
     *
     * @var array
     */
    private $_batches = array();
 
    /**
     * $_activeBatch object
     *
     * @var array
     */
    private $_activeBatch = null;
 
    /**
     * $_captureBatch boolean
     *
     * @var boolean
     */
    private $_captureBatch = false;
 
    /**
     * $_batchRequest boolean
     *
     * @var boolean
     */
    private $_batchRequest = false;
 
    /**
     * custom queue name (leave empty if no custom queue is required)
     *
     * @var string
     */
    private $_customQueue = null;
 
    /**
     * $_database string
     *
     * @var string
     */
    private $_database = '';
 
    /**
     * Set up the connection object, validate the options provided
     *
     * @throws Exception
     *
     * @param array $options - initial connection options
     *
     * @return Connection
     */
    public function __construct(array $options)
    {
        $this->_options      = new ConnectionOptions($options);
        $this->_useKeepAlive = ($this->_options[ConnectionOptions::OPTION_CONNECTION] === 'Keep-Alive');
        $this->setDatabase($this->_options[ConnectionOptions::OPTION_DATABASE]);
    }
 
    /**
     * Close existing connection handle if a keep-alive connection was used
     *
     * @return void
     */
    public function __destruct()
    {
        if ($this->_useKeepAlive && is_resource($this->_handle)) {
            @fclose($this->_handle);
        }
    }
 
    /**
     * Get an option set for the connection
     *
     * @throws ClientException
     *
     * @param string $name - name of option
     *
     * @return mixed
     */
    public function getOption($name)
    {
        assert(is_string($name));
 
        return $this->_options[$name];
    }
 
    /**
     * Set an option set for the connection
     *
     * @throws ClientException
     *
     * @param string $name - name of option
     * @param string $value - value of option
     */
    public function setOption($name, $value) {
        if ($name === ConnectionOptions::OPTION_ENDPOINT ||
            $name === ConnectionOptions::OPTION_HOST ||
            $name === ConnectionOptions::OPTION_PORT) {
            throw new ClientException('Must not set option ' . $value . ' after connection is created.');
        }
 
        $this->_options[$name] = $value;
 
        // special handling for several options
        if ($name === ConnectionOptions::OPTION_TIMEOUT) {
            // set the timeout option: patch the stream of an existing connection
            if (is_resource($this->_handle)) {
                stream_set_timeout($this->_handle, $value); 
            }
        }
        else if ($name === ConnectionOptions::OPTION_CONNECTION) {
          // set keep-alive flag
          $this->_useKeepAlive = (strtolower($value) === 'keep-alive');
        }
        else if ($name === ConnectionOptions::OPTION_DATABASE) {
          // set database
          $this->setDatabase($value);
        }
    }
 
 
    /**
     * Enables a custom queue name for all actions of the connection
     *
     * @param string $queueName - queue name
     * @param number $count - number of requests the custom queue will be used for
     * @internal this method is currently experimental. whether or not it will 
     *           become part of the official API needs decision
     */
 
    public function enableCustomQueue($queueName, $count = null) 
    {
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] = $queueName;
 
        if ($count !== null) {
            if (! is_numeric($count) || $count <= 0) {
                throw new ClientException('Invalid value for count value of custom queues');
            }
            $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT] = $count;
        }
    }
 
    /**
     * Disable usage of custom queue for all actions of the connection
     *
     * @internal this method is currently experimental. whether or not it will 
     *           become part of the official API needs decision
     */
    public function disableCustomQueue() 
    {
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] = null;
        $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT] = null;
    }
 
 
    /**
     * Issue an HTTP GET request
     *
     * @throws Exception
     *
     * @param string $url - GET URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function get($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_GET, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP POST request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - POST URL
     * @param string $data - body to post
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function post($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_POST, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP PUT request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - PUT URL
     * @param string $data - body to post
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function put($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_PUT, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP Head request with the data provided
     *
     * @throws Exception
     *
     * @param string $url - PUT URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function head($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_HEAD, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP PATCH request with the data provided
     *
     * @throws Exception
     *
     * @param string $url  - PATCH URL
     * @param string $data - patch body
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function patch($url, $data, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_PATCH, $url, $data, $customHeaders);
 
        return $this->parseResponse($response);
    }
 
    /**
     * Issue an HTTP DELETE request with the data provided
     *
     * @throws Exception
     *
     * @param string $url - DELETE URL
     * @param array  $customHeader
     *
     * @return HttpResponse
     */
    public function delete($url, array $customHeaders = array())
    {
        $response = $this->executeRequest(HttpHelper::METHOD_DELETE, $url, '', $customHeaders);
 
        return $this->parseResponse($response);
    }
 
 
    /**
     * Get a connection handle
     *
     * If keep-alive connections are used, the handle will be stored and re-used
     *
     * @throws ClientException
     * @return resource - connection handle
     */
    private function getHandle()
    {
        if ($this->_useKeepAlive && $this->_handle && is_resource($this->_handle)) {
            // keep-alive and handle was created already
            $handle = $this->_handle;
 
            // check if connection is still valid
            if (!feof($handle)) {
                // connection still valid
                return $handle;
            }
 
            // close handle
            @fclose($this->_handle);
            $this->_handle = 0;
 
            if (!$this->_options[ConnectionOptions::OPTION_RECONNECT]) {
                // if reconnect option not set, this is the end
                throw new ClientException('Server has closed the connection already.');
            }
        }
 
        // no keep-alive or no handle available yet or a reconnect
        $handle = HttpHelper::createConnection($this->_options);
 
        if ($this->_useKeepAlive && is_resource($handle)) {
            $this->_handle = $handle;
        }
 
        return $handle;
    }
 
    /**
     * Parse the response return the body values as an assoc array
     *
     * @throws Exception
     *
     * @param HttpResponse $response - the response as supplied by the server
     *
     * @return HttpResponse
     */
    public function parseResponse(HttpResponse $response)
    {
        $httpCode = $response->getHttpCode();
 
        if ($httpCode < 200 || $httpCode >= 400) {
            // failure on server
 
            $body = $response->getBody();
            if ($body != '') {
                // check if we can find details in the response body
                $details = json_decode($body, true);
                if (is_array($details) && isset($details["errorMessage"])) {
                    // yes, we got details
                    $exception = new ServerException($details["errorMessage"], $details["code"]);
                    $exception->setDetails($details);
                    throw $exception;
                }
            }
 
            // no details found, throw normal exception
            throw new ServerException($response->getResult(), $httpCode);
        }
 
        return $response;
    }
 
    /**
     * Execute an HTTP request and return the results
     *
     * This function will throw if no connection to the server can be established or if
     * there is a problem during data exchange with the server.
     *
     * will restore it.
     *
     * @throws Exception
     *
     * @param string $method        - HTTP request method
     * @param string $url           - HTTP URL
     * @param string $data          - data to post in body
     * @param array  $customHeaders - any array containing header elements
     *
     * @return HttpResponse
     */
    private function executeRequest($method, $url, $data, array $customHeaders = array())
    {
        HttpHelper::validateMethod($method);
        $database = $this->getDatabase();
        if ($database === '') {
            $url = '/_db/' . '_system' . $url;
        } else {
            $url = '/_db/' . urlencode($database) . $url;
        }
 
        // check if a custom queue should be used
        if (! isset($customHeaders[ConnectionOptions::OPTION_CUSTOM_QUEUE]) &&
            $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE] !== null) {
 
            $customHeaders[HttpHelper::QUEUE_HEADER] = $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE]; 
 
            // check if a counter is set for the custom queue
            $count = $this->_options[ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT];
            if ($count !== null) {
                // yes, now decrease the counter
 
                if ($count === 1) {
                    $this->disableCustomQueue();
                }
                else {
                    $this->_options->offsetSet(ConnectionOptions::OPTION_CUSTOM_QUEUE_COUNT, $count - 1);
                }
            }
        }
 
        // create request data
        if ($this->_batchRequest === false) {
 
            if ($this->_captureBatch === true) {
                $this->_options->offsetSet(ConnectionOptions::OPTION_BATCHPART, true);
                $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
                $this->_options->offsetSet(ConnectionOptions::OPTION_BATCHPART, false);
            } else {
                $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
            }
 
            if ($this->_captureBatch === true) {
                $batchPart = $this->doBatch($method, $request);
                if (!is_null($batchPart)) {
                    return $batchPart;
                }
            }
        } else {
            $this->_batchRequest = false;
 
            $this->_options->offsetSet(ConnectionOptions::OPTION_BATCH, true);
 
            $request = HttpHelper::buildRequest($this->_options, $method, $url, $data, $customHeaders);
            $this->_options->offsetSet(ConnectionOptions::OPTION_BATCH, false);
        }
 
 
        $traceFunc = $this->_options[ConnectionOptions::OPTION_TRACE];
        if ($traceFunc) {
            // call tracer func
            if ($this->_options[ConnectionOptions::OPTION_ENHANCED_TRACE]) {
                list($header) = HttpHelper::parseHttpMessage($request, $url, $method);
                $headers = HttpHelper::parseHeaders($header);
                $traceFunc(new TraceRequest($headers[2], $method, $url, $data));
            } else {
                $traceFunc('send', $request);
            }
        }
 
 
        // open the socket. note: this might throw if the connection cannot be established
        $handle = $this->getHandle();
 
        if ($handle) {
            // send data and get response back
 
            if ($traceFunc) {
                // only issue syscall if we need it
                $startTime = microtime(true);
            }
 
            $result = HttpHelper::transfer($handle, $request);
 
            if ($traceFunc) {
                // only issue syscall if we need it
                $timeTaken = microtime(true) - $startTime;
            }
 
            $status = socket_get_status($handle);
            if ($status['timed_out']) {
                throw new ClientException('Got a timeout while waiting for the server\'s response', 408);
            }
 
            if (!$this->_useKeepAlive) {
                // must close the connection
                fclose($handle);
            }
 
            $response = new HttpResponse($result, $url, $method);
 
            if ($traceFunc) {
                // call tracer func
                if ($this->_options[ConnectionOptions::OPTION_ENHANCED_TRACE]) {
                    $traceFunc(
                        new TraceResponse($response->getHeaders(), $response->getHttpCode(), $response->getBody(),
                            $timeTaken)
                    );
                } else {
                    $traceFunc('receive', $result);
                }
            }
 
            return $response;
        }
 
        throw new ClientException('Whoops, this should never happen');
    }
 
    /**
     * Get the client version (alias for getClientVersion)
     *
     * @return string
     */
    public static function getVersion()
    {
        return self::getClientVersion();
    }
 
 
    /**
     * Get the client version
     *
     * @return string
     */
    public static function getClientVersion()
    {
        return self::$_apiVersion;
    }
 
    /**
     * Stop capturing commands
     *
     * @return Batch - Returns the active batch object
     */
    public function stopCaptureBatch()
    {
        $this->_captureBatch = false;
 
        return $this->getActiveBatch();
    }
 
 
    /**
     * returns the active batch
     *
     * @return Batch active batch
     */
    public function getActiveBatch()
    {
        return $this->_activeBatch;
    }
 
    /**
     * Sets the active Batch for this connection
     *
     * @param Batch $batch - Sets the given batch as active
     *
     * @return Batch active batch
     */
    public function setActiveBatch($batch)
    {
        $this->_activeBatch = $batch;
 
        return $this->_activeBatch;
    }
 
 
    /**
     * Sets the batch capture state (true, if capturing)
     *
     * @param boolean $state true to turn on capture batch mode, false to turn it off
     */
    public function setCaptureBatch($state)
    {
        $this->_captureBatch = $state;
    }
 
 
    /**
     * Sets connection into Batch-request mode. This is needed for some operations to act differently when in this mode.
     *
     * @param boolean $state sets the connection state to batch request, meaning it is currently doing a batch request.
     */
    public function setBatchRequest($state)
    {
        $this->_batchRequest = $state;
    }
 
 
    /**
     * Returns true if this connection is in Batch-Capture mode
     *
     * @return bool
     *
     * returns the active batch
     */
    public function isInBatchCaptureMode()
    {
        return $this->_captureBatch;
    }
 
 
    /**
     * returns the active batch
     *
     */
    public function getBatches()
    {
        return $this->_batches;
    }
 
 
    /**
     * This is a helper function to executeRequest that captures requests if we're in batch mode
     *
     * @param mixed  $method  - The method of the request (GET, POST...)
     *
     * @param string $request - The request to process
     *
     * This checks if we're in batch mode and returns a placeholder object,
     * since we need to return some object that is expected by the caller.
     * if we're not in batch mode it doesn't return anything, and
     *
     * @return mixed Batchpart or null if not in batch capturing mode
     */
    private function doBatch($method, $request)
    {
        $batchPart = null;
        if ($this->_captureBatch === true) {
 
            /** @var $batch Batch */
            $batch = $this->getActiveBatch();
 
            $batchPart = $batch->append($method, $request);
        }
 
        # do batch processing
        return $batchPart;
    }
 
 
    /**
     * This function checks that the encoding of a string is utf.
     * It only checks for printable characters.
     *
     *
     * @param array $string the data to check
     *
     * @return boolean true if string is UTF-8, false if not
     */
    public static function detect_utf($string)
    {
        if (preg_match("//u", $string)) {
            return true;
        } else {
            return false;
        }
    }
 
 
    /**
     * This function checks that the encoding of the keys and
     * values of the array are utf-8, recursively.
     * It will raise an exception if it encounters wrong encoded strings.
     *
     * @param array $data the data to check
     *
     * @throws ClientException
     */
    public static function check_encoding($data)
    {
        foreach ($data as $key => $value) {
            if (!is_array($value)) {
                // check if the multibyte library function is installed and use it.
                if (function_exists('mb_detect_encoding')) {
                    // check with mb library
                    if (mb_detect_encoding($key, 'UTF-8', true) === false) {
                        throw new ClientException("Only UTF-8 encoded keys allowed. Wrong encoding in key string: " . $key);
                    }
                    if (mb_detect_encoding($value, 'UTF-8', true) === false) {
                        throw new ClientException("Only UTF-8 encoded values allowed. Wrong encoding in value string: " . $value);
                    }
                } else {
                    // fallback to preg_match checking
                    if (self::detect_utf($key) == false) {
                        throw new ClientException("Only UTF-8 encoded keys allowed. Wrong encoding in key string: " . $key);
                    }
                    if (self::detect_utf($value) == false) {
                        throw new ClientException("Only UTF-8 encoded values allowed. Wrong encoding in value string: " . $value);
                    }
                }
            } else {
                self::check_encoding($value);
            }
        }
    }
 
 
    /**
     * This is a json_encode() wrapper that also checks if the data is utf-8 conform.
     * internally it calls the check_encoding() method. If that method does not throw
     * an Exception, this method will happily return the json_encoded data.
     *
     * @param mixed $data    the data to encode
     * @param mixed $options the options for the json_encode() call
     *
     * @return string the result of the json_encode
     */
    public function json_encode_wrapper($data, $options = null)
    {
        if ($this->_options[ConnectionOptions::OPTION_CHECK_UTF8_CONFORM] === true) {
            self::check_encoding($data);
        }
        if (empty($data)) {
            $response = json_encode($data, $options | JSON_FORCE_OBJECT);
        } else {
            $response = json_encode($data, $options);
        }
 
        return $response;
    }
 
 
    /**
     * Set the database to use with this connection
     *
     * Sets the database to use with this connection, for example: 'my_database'<br>
     * Further calls to the database will be addressed to the given database.
     *
     * @param string $database the database to use
     */
    public function setDatabase($database)
    {
        $this->_options[ConnectionOptions::OPTION_DATABASE] = $database;
        $this->_database = $database;
    }
 
    /**
     * Get the database that is currently used with this connection
     *
     * Get the database to use with this connection, for example: 'my_database'
     *
     * @return string
     */
    public function getDatabase()
    {
        return $this->_database;
    }
}
#3triagens\ArangoDb\Connection->get(/_api/document/shorten/RxQ4Pq17, Array())
/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/DocumentHandler.php (169)
<?php
 
/**
 * ArangoDB PHP client: document handler
 *
 * @package   triagens\ArangoDb
 * @author    Jan Steemann
 * @author    Frank Mayer
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */
 
namespace triagens\ArangoDb;
 
 
/**
 * A handler that manages documents
 *
 * A document handler that fetches documents from the server and
 * persists them on the server. It does so by issuing the
 * appropriate HTTP requests to the server.<br>
 *
 * <br>
 *
 * @package   triagens\ArangoDb
 * @since     0.2
 */
class DocumentHandler extends
    Handler
{
    /**
     * documents array index
     */
    const ENTRY_DOCUMENTS = 'documents';
 
    /**
     * collection parameter
     */
    const OPTION_COLLECTION = 'collection';
 
    /**
     * example parameter
     */
    const OPTION_EXAMPLE = 'example';
 
 
    /**
     * Get a single document from a collection
     *
     * Alias method for getById()
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'revision' - the documents revision</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    public function get($collectionId, $documentId, array $options = array())
    {
        return $this->getById($collectionId, $documentId, $options);
    }
 
 
    /**
     * Check if a document exists
     *
     * This will call self::get() internally and checks if there
     * was an exception thrown which represents an 404 request.
     *
     * @throws Exception When any other error than a 404 occurs
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed  $documentId   - document identifier
     * @return boolean
     */
    public function has($collectionId, $documentId)
    {
        try {
            // will throw ServerException if entry could not be retrieved
            $result = $this->get($collectionId, $documentId);
            return true;
        } catch (ServerException $e) {
            // we are expecting a 404 to return boolean false
            if ($e->getCode() === 404) {
                return false;
            }
 
            // just rethrow
            throw $e;
        }
 
        return false;
    }
 
 
    /**
     * Get a single document from a collection
     *
     * This will throw if the document cannot be fetched from the server.
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            <li>'revision' - The document is returned if it matches/not matches revision.</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    public function getById($collectionId, $documentId, array $options = array())
    {
        $data = $this->getDocument(Urls::URL_DOCUMENT, $collectionId, $documentId, $options);
        $options['_isNew'] = false;
 
        return $this->createFromArrayWithContext($data, $options);
    }
 
 
    /**
     * Get a single document (internal method)
     *
     * This method is the workhorse for getById() in this handler and the edges handler
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            <li>'revision' - The document is returned if it matches/not matches revision.</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    protected function getDocument($url, $collectionId, $documentId, array $options = array())
    {
        $url      = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $headerElements = array();
        if (array_key_exists("ifMatch", $options) && array_key_exists("revision", $options)) {
            if ($options["ifMatch"] === true) {
                $headerElements["If-Match"] = '"' . $options["revision"] .'"';
            } else {
                $headerElements["If-None-Match"] = '"' . $options["revision"]. '"';
            }
        }
 
        $response = $this->getConnection()->get($url, $headerElements);
 
        if ($response->getHttpCode() === 304) {
            throw new ClientException('Document has not changed.');
        }
 
        return  $response->getJson();
    }
 
 
    /**
     * Gets information about a single documents from a collection
     *
     * This will throw if the document cannot be fetched from the server
     *
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number.
     * @param mixed $documentId   - document identifier.
     * @param boolean ifMatch     -  boolean if given revision should match or not.
     * @param string revision     - The document is returned if it matches/not matches revision.
     *
     * @return array - an array containing the complete header including the key httpCode.
     */
    public function getHead($collectionId, $documentId, $revision = null, $ifMatch = null)
    {
        return $this->head(Urls::URL_DOCUMENT, $collectionId, $documentId, $revision, $ifMatch);
    }
 
 
    /**
     * Get meta-data for a single document (internal method)
     *
     * This method is the workhorse for getHead() in this handler and the edges handler
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId    - document identifier
     * @param mixed $revision      - optional document revision
     * @param boolean ifMatch      -  boolean if given revision should match or not.
     *
     * @return array - the document meta-data
     */
    protected function head($url, $collectionId, $documentId, $revision = null, $ifMatch = null) {
        $url      = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $headerElements = array();
        if ($revision != null && $ifMatch !== null) {
            if ($ifMatch) {
                $headerElements["If-Match"] = '"' . $revision .'"';
            } else {
                $headerElements["If-None-Match"] = '"' . $revision . '"';
            }
        }
 
        $response = $this->getConnection()->head($url, $headerElements);
        $headers = $response->getHeaders();
        $headers["httpCode"] = $response->getHttpCode();
        return $headers;
    }
 
 
    /**
     * Intermediate function to call the createFromArray function from the right context
     *
     * @param $data
     * @param $options
     *
     * @return Document
     */
    protected function createFromArrayWithContext($data, $options)
    {
        return Document::createFromArray($data, $options);
    }
 
 
    /**
     * Get the list of all documents' ids from a collection
     *
     * This will throw if the list cannot be fetched from the server
     *
     * @throws Exception
     *
     * @param mixed $collectionId - collection id as string or number
     *
     * @return array - ids of documents in the collection
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by  CollectionHandler::getAllIds()
     *
     */
    public function getAllIds($collectionId)
    {
        $collectionHandler = new CollectionHandler($this->getConnection());
 
        return $collectionHandler->getAllIds($collectionId);
    }
 
 
    /**
     * Get document(s) by specifying an example
     *
     * This will throw if the list cannot be fetched from the server
     *
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param mixed      $document     - the example document as a Document object or an array
     * @param bool|array $options      - optional, prior to v1.0.0 this was a boolean value for sanitize, since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'_sanitize' - True to remove _id and _rev attributes from result documents. Defaults to false.</li>
     *                                 <li>'sanitize' - Deprecated, please use '_sanitize'.</li>
     *                                 <li>'_hiddenAttributes' - Set an array of hidden attributes for created documents.
     *                                 <li>'hiddenAttributes' - Deprecated, please use '_hiddenAttributes'.</li>
     *                                 <p>
     *                                 This is actually the same as setting hidden attributes using setHiddenAttributes() on a document. <br>
     *                                 The difference is, that if you're returning a resultset of documents, the getAll() is already called <br>
     *                                 and the hidden attributes would not be applied to the attributes.<br>
     *                                 </p>
     *                                 </li>
     *                                 <li>'batchSize' - can optionally be used to tell the server to limit the number of results to be transferred in one batch</li>
     *                                 <li>'skip' -  Optional, The number of documents to skip in the query.</li>
     *                                 <li>'limit' -  Optional, The maximal amount of documents to return. 'skip' is applied before the limit restriction.</li>
     *                                 </p>
     *
     * @return cursor - Returns a cursor containing the result
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by CollectionHandler::byExample()
     */
    public function getByExample($collectionId, $document, $options = false)
    {
        $collectionHandler = new CollectionHandler($this->getConnection());
 
        return $collectionHandler->byExample($collectionId, $document, $options);
    }
 
 
    /**
     * Add a document to a collection
     *
     * This will add the document to the collection and return the document's id
     *
     * This will throw if the document cannot be created
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param Document   $document     - the document to be added
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by save()
     *
     */
 
    public function add($collectionId, Document $document, $options = array())
    {
        return $this->save($collectionId, $document, $options);
    }
 
    /**
     * Store a document to a collection
     *
     * This is an alias/shortcut to save() and replace(). Instead of having to determine which of the 3 functions to use,
     * simply pass the document to store() and it will figure out which one to call.
     *
     * This will throw if the document cannot be saved or replaced.
     *
     * @throws Exception
     *
     * @param Document   $document     - the document to be added, can be passed as a document or an array
     * @param mixed      $collectionId - collection id as string or number
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.2.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     * @since 1.0
     */
    public function store(Document $document, $collectionId = null, $options = array())
    {
        if ($document->getIsNew()) {
 
            if ($collectionId == null) {
                throw new ClientException('A collection id is required to store a new document.');
            }
 
            $result = $this->save($collectionId, $document, $options);
            $document->setIsNew(false);
 
            return $result;
        } else {
 
            if ($collectionId) {
                throw new ClientException('An existing document cannot be stored into a new collection');
            }
 
            return $this->replace($document, $options);
        }
    }
 
 
    /**
     * save a document to a collection
     *
     * This will add the document to the collection and return the document's id
     *
     * This will throw if the document cannot be saved
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param mixed      $document     - the document to be added, can be passed as a document or an array
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     * @since 1.0
     */
    public function save($collectionId, $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        } 
        // This preserves compatibility for the old create parameter.
        $params = array(self::OPTION_COLLECTION => $collectionId);
        $params = $this->validateAndIncludeOldSingleParameterInParams(
                       $options,
                       $params,
                       ConnectionOptions::OPTION_CREATE
        );
 
        $params = $this->includeOptionsInParams(
                       $options,
                       $params,
                       array(
                            ConnectionOptions::OPTION_WAIT_SYNC => $this->getConnectionOption(
                                                                        ConnectionOptions::OPTION_WAIT_SYNC
                                ),
                       )
        );
 
        if (is_array($document)) {
            $document = Document::createFromArray($document);
        }
        $data = $document->getAll();
 
        $url = UrlHelper::appendParamsUrl(Urls::URL_DOCUMENT, $params);
 
        $response = $this->getConnection()->post($url, $this->json_encode_wrapper($data));
 
        $location = $response->getLocationHeader();
        if (!$location) {
            throw new ClientException('Did not find location header in server response');
        }
 
        $json = $response->getJson();
        $id   = UrlHelper::getDocumentIdFromLocation($location);
 
        $document->setInternalId($json[Document::ENTRY_ID]);
        $document->setRevision($json[Document::ENTRY_REV]);
 
        if ($id != $document->getId()) {
            throw new ClientException('Got an invalid response from the server');
        }
 
        $document->setIsNew(false);
 
        return $document->getId();
    }
 
 
    /**
     * Update an existing document in a collection, identified by the including _id and optionally _rev in the patch document.
     * Attention - The behavior of this method has changed since version 1.1
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the document to-be-replaced is the same as the one given.
     *
     * @throws Exception
     *
     * @param Document $document - The patch document that will update the document in question
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function update(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        return $this->updateById($collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Update an existing document in a collection, identified by collection id and document id
     * Attention - The behavior of this method has changed since version 1.1
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the document to-be-updated is the same as the one given.
     *
     * @throws Exception
     *
     * @param string    $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - patch document which contains the attributes and values to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function updateById($collectionId, $documentId, Document $document, $options = array())
    {
        return $this->patch(Urls::URL_DOCUMENT, $collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Update an existing document in a collection (internal method)
     *
     * @throws Exception
     *
     * @param string   $url          - server-side URL being called
     * @param string   $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - patch document which contains the attributes and values to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function patch($url, $collectionId, $documentId, Document $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_UPDATE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array(
                'waitForSync' => $this->getConnectionOption(ConnectionOptions::OPTION_WAIT_SYNC),
                'keepNull'    => true,
            )
        );
 
        $revision = $document->getRevision();
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $url    = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url    = UrlHelper::appendParamsUrl($url, $params);
        $result = $this->getConnection()->patch($url, $this->json_encode_wrapper($document->getAll()));
        $json   = $result->getJson();
        $document->setRevision($json[Document::ENTRY_REV]);
 
        return true;
    }
 
 
    /**
     * Replace an existing document in a collection, identified by the document itself
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the to-be-replaced document is the same as the one given.
     *
     * @throws Exception
     *
     * @param Document $document - document to be updated
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function replace(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        return $this->replaceById($collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Replace an existing document in a collection, identified by collection id and document id
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be Replaced
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the to-be-replaced document is the same as the one given.
     *
     * @throws Exception
     *
     * @param mixed    $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - document to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function replaceById($collectionId, $documentId, Document $document, $options = array())
    {
        return $this->put(Urls::URL_DOCUMENT, $collectionId, $documentId, $document, $options);
    }
   
    
    /**
     * Replace an existing document in a collection (internal method)
     *
     * @throws Exception
     *
     * @param string   $url          - the server-side URL being called
     * @param string   $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - document to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function put($url, $collectionId, $documentId, Document $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_REPLACE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array('waitForSync' => ConnectionOptions::OPTION_WAIT_SYNC)
        );
 
        $revision = $document->getRevision();
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $data   = $document->getAll();
        $url    = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url    = UrlHelper::appendParamsUrl($url, $params);
        $result = $this->getConnection()->put($url, $this->json_encode_wrapper($data));
        $json   = $result->getJson();
        $document->setRevision($json[Document::ENTRY_REV]);
 
        return true;
    }
 
    /**
     * Delete a document from a collection, identified by the document itself
     *
     * @throws Exception
     *
     * @param Document $document - document to be updated
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by remove()
     *
     */
    public function delete(Document $document, $options = array())
    {
        return $this->remove($document, $options);
    }
 
 
    /**
     * Remove a document from a collection, identified by the document itself
     *
     * @throws Exception
     *
     * @param Document $document - document to be removed
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function remove(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        $revision = $this->getRevision($document);
 
        return $this->deleteById($collectionId, $documentId, $revision, $options);
    }
 
 
    /**
     * Delete a document from a collection, identified by the collection id and document id
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param  mixed $revision     - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by removeById()
     */
    public function deleteById($collectionId, $documentId, $revision = null, $options = array())
    {
        $this->removeById($collectionId, $documentId, $revision, $options);
 
        return true;
    }
 
 
    /**
     * Remove a document from a collection, identified by the collection id and document id
     *
     * @throws Exception
     *
     * @param mixed  $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param  mixed $revision     - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function removeById($collectionId, $documentId, $revision = null, $options = array())
    {
       return $this->erase(Urls::URL_DOCUMENT, $collectionId, $documentId, $revision, $options);
    }
 
    
    /**
     * Remove a document from a collection (internal method)
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param mixed $revision      - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function erase($url, $collectionId, $documentId, $revision = null, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_DELETE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array('waitForSync' => ConnectionOptions::OPTION_WAIT_SYNC)
        );
 
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $url = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url = UrlHelper::appendParamsUrl($url, $params);
        $this->getConnection()->delete($url);
 
        return true;
    }
 
 
    /**
     * Helper function to get a document id from a document or a document id value
     *
     * @throws ClientException
     *
     * @param mixed $document - document id OR document to be updated
     *
     * @return mixed - document id, will throw if there is an error
     */
    private function getDocumentId($document)
    {
        if ($document instanceof Document) {
            $documentId = $document->getId();
        } else {
            $documentId = $document;
        }
 
        if (trim($documentId) === "" || !(is_string($documentId) || is_double($documentId) || is_int($documentId))) {
            throw new ClientException('Cannot alter a document without a document id');
        }
 
        return $documentId;
    }
 
 
    /**
     * Helper function to get a document id from a document or a document id value
     *
     * @throws ClientException
     *
     * @param mixed $document - document id OR document to be updated
     *
     * @return mixed - document id, will throw if there is an error
     */
    private function getRevision($document)
    {
        if ($document instanceof Document) {
            $revision = $document->getRevision();
        } else {
            $revision = null;
        }
 
        return $revision;
    }
 
 
    /**
     * Helper function to get a collection id from a document
     *
     * @throws ClientException
     *
     * @param Document $document - document id
     *
     * @return mixed - collection id, will throw if there is an error
     */
    private function getCollectionId(Document $document)
    {
        $collectionId = $document->getCollectionId();
 
        if (!$collectionId || !(is_string($collectionId) || is_double($collectionId) || is_int($collectionId))) {
            throw new ClientException('Cannot alter a document without a document id');
        }
 
        return $collectionId;
    }
}
#4triagens\ArangoDb\DocumentHandler->getDocument(/_api/document, shorten, RxQ4Pq17, Array())
/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/DocumentHandler.php (128)
<?php
 
/**
 * ArangoDB PHP client: document handler
 *
 * @package   triagens\ArangoDb
 * @author    Jan Steemann
 * @author    Frank Mayer
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */
 
namespace triagens\ArangoDb;
 
 
/**
 * A handler that manages documents
 *
 * A document handler that fetches documents from the server and
 * persists them on the server. It does so by issuing the
 * appropriate HTTP requests to the server.<br>
 *
 * <br>
 *
 * @package   triagens\ArangoDb
 * @since     0.2
 */
class DocumentHandler extends
    Handler
{
    /**
     * documents array index
     */
    const ENTRY_DOCUMENTS = 'documents';
 
    /**
     * collection parameter
     */
    const OPTION_COLLECTION = 'collection';
 
    /**
     * example parameter
     */
    const OPTION_EXAMPLE = 'example';
 
 
    /**
     * Get a single document from a collection
     *
     * Alias method for getById()
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'revision' - the documents revision</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    public function get($collectionId, $documentId, array $options = array())
    {
        return $this->getById($collectionId, $documentId, $options);
    }
 
 
    /**
     * Check if a document exists
     *
     * This will call self::get() internally and checks if there
     * was an exception thrown which represents an 404 request.
     *
     * @throws Exception When any other error than a 404 occurs
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed  $documentId   - document identifier
     * @return boolean
     */
    public function has($collectionId, $documentId)
    {
        try {
            // will throw ServerException if entry could not be retrieved
            $result = $this->get($collectionId, $documentId);
            return true;
        } catch (ServerException $e) {
            // we are expecting a 404 to return boolean false
            if ($e->getCode() === 404) {
                return false;
            }
 
            // just rethrow
            throw $e;
        }
 
        return false;
    }
 
 
    /**
     * Get a single document from a collection
     *
     * This will throw if the document cannot be fetched from the server.
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            <li>'revision' - The document is returned if it matches/not matches revision.</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    public function getById($collectionId, $documentId, array $options = array())
    {
        $data = $this->getDocument(Urls::URL_DOCUMENT, $collectionId, $documentId, $options);
        $options['_isNew'] = false;
 
        return $this->createFromArrayWithContext($data, $options);
    }
 
 
    /**
     * Get a single document (internal method)
     *
     * This method is the workhorse for getById() in this handler and the edges handler
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            <li>'revision' - The document is returned if it matches/not matches revision.</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    protected function getDocument($url, $collectionId, $documentId, array $options = array())
    {
        $url      = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $headerElements = array();
        if (array_key_exists("ifMatch", $options) && array_key_exists("revision", $options)) {
            if ($options["ifMatch"] === true) {
                $headerElements["If-Match"] = '"' . $options["revision"] .'"';
            } else {
                $headerElements["If-None-Match"] = '"' . $options["revision"]. '"';
            }
        }
 
        $response = $this->getConnection()->get($url, $headerElements);
 
        if ($response->getHttpCode() === 304) {
            throw new ClientException('Document has not changed.');
        }
 
        return  $response->getJson();
    }
 
 
    /**
     * Gets information about a single documents from a collection
     *
     * This will throw if the document cannot be fetched from the server
     *
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number.
     * @param mixed $documentId   - document identifier.
     * @param boolean ifMatch     -  boolean if given revision should match or not.
     * @param string revision     - The document is returned if it matches/not matches revision.
     *
     * @return array - an array containing the complete header including the key httpCode.
     */
    public function getHead($collectionId, $documentId, $revision = null, $ifMatch = null)
    {
        return $this->head(Urls::URL_DOCUMENT, $collectionId, $documentId, $revision, $ifMatch);
    }
 
 
    /**
     * Get meta-data for a single document (internal method)
     *
     * This method is the workhorse for getHead() in this handler and the edges handler
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId    - document identifier
     * @param mixed $revision      - optional document revision
     * @param boolean ifMatch      -  boolean if given revision should match or not.
     *
     * @return array - the document meta-data
     */
    protected function head($url, $collectionId, $documentId, $revision = null, $ifMatch = null) {
        $url      = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $headerElements = array();
        if ($revision != null && $ifMatch !== null) {
            if ($ifMatch) {
                $headerElements["If-Match"] = '"' . $revision .'"';
            } else {
                $headerElements["If-None-Match"] = '"' . $revision . '"';
            }
        }
 
        $response = $this->getConnection()->head($url, $headerElements);
        $headers = $response->getHeaders();
        $headers["httpCode"] = $response->getHttpCode();
        return $headers;
    }
 
 
    /**
     * Intermediate function to call the createFromArray function from the right context
     *
     * @param $data
     * @param $options
     *
     * @return Document
     */
    protected function createFromArrayWithContext($data, $options)
    {
        return Document::createFromArray($data, $options);
    }
 
 
    /**
     * Get the list of all documents' ids from a collection
     *
     * This will throw if the list cannot be fetched from the server
     *
     * @throws Exception
     *
     * @param mixed $collectionId - collection id as string or number
     *
     * @return array - ids of documents in the collection
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by  CollectionHandler::getAllIds()
     *
     */
    public function getAllIds($collectionId)
    {
        $collectionHandler = new CollectionHandler($this->getConnection());
 
        return $collectionHandler->getAllIds($collectionId);
    }
 
 
    /**
     * Get document(s) by specifying an example
     *
     * This will throw if the list cannot be fetched from the server
     *
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param mixed      $document     - the example document as a Document object or an array
     * @param bool|array $options      - optional, prior to v1.0.0 this was a boolean value for sanitize, since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'_sanitize' - True to remove _id and _rev attributes from result documents. Defaults to false.</li>
     *                                 <li>'sanitize' - Deprecated, please use '_sanitize'.</li>
     *                                 <li>'_hiddenAttributes' - Set an array of hidden attributes for created documents.
     *                                 <li>'hiddenAttributes' - Deprecated, please use '_hiddenAttributes'.</li>
     *                                 <p>
     *                                 This is actually the same as setting hidden attributes using setHiddenAttributes() on a document. <br>
     *                                 The difference is, that if you're returning a resultset of documents, the getAll() is already called <br>
     *                                 and the hidden attributes would not be applied to the attributes.<br>
     *                                 </p>
     *                                 </li>
     *                                 <li>'batchSize' - can optionally be used to tell the server to limit the number of results to be transferred in one batch</li>
     *                                 <li>'skip' -  Optional, The number of documents to skip in the query.</li>
     *                                 <li>'limit' -  Optional, The maximal amount of documents to return. 'skip' is applied before the limit restriction.</li>
     *                                 </p>
     *
     * @return cursor - Returns a cursor containing the result
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by CollectionHandler::byExample()
     */
    public function getByExample($collectionId, $document, $options = false)
    {
        $collectionHandler = new CollectionHandler($this->getConnection());
 
        return $collectionHandler->byExample($collectionId, $document, $options);
    }
 
 
    /**
     * Add a document to a collection
     *
     * This will add the document to the collection and return the document's id
     *
     * This will throw if the document cannot be created
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param Document   $document     - the document to be added
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by save()
     *
     */
 
    public function add($collectionId, Document $document, $options = array())
    {
        return $this->save($collectionId, $document, $options);
    }
 
    /**
     * Store a document to a collection
     *
     * This is an alias/shortcut to save() and replace(). Instead of having to determine which of the 3 functions to use,
     * simply pass the document to store() and it will figure out which one to call.
     *
     * This will throw if the document cannot be saved or replaced.
     *
     * @throws Exception
     *
     * @param Document   $document     - the document to be added, can be passed as a document or an array
     * @param mixed      $collectionId - collection id as string or number
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.2.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     * @since 1.0
     */
    public function store(Document $document, $collectionId = null, $options = array())
    {
        if ($document->getIsNew()) {
 
            if ($collectionId == null) {
                throw new ClientException('A collection id is required to store a new document.');
            }
 
            $result = $this->save($collectionId, $document, $options);
            $document->setIsNew(false);
 
            return $result;
        } else {
 
            if ($collectionId) {
                throw new ClientException('An existing document cannot be stored into a new collection');
            }
 
            return $this->replace($document, $options);
        }
    }
 
 
    /**
     * save a document to a collection
     *
     * This will add the document to the collection and return the document's id
     *
     * This will throw if the document cannot be saved
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param mixed      $document     - the document to be added, can be passed as a document or an array
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     * @since 1.0
     */
    public function save($collectionId, $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        } 
        // This preserves compatibility for the old create parameter.
        $params = array(self::OPTION_COLLECTION => $collectionId);
        $params = $this->validateAndIncludeOldSingleParameterInParams(
                       $options,
                       $params,
                       ConnectionOptions::OPTION_CREATE
        );
 
        $params = $this->includeOptionsInParams(
                       $options,
                       $params,
                       array(
                            ConnectionOptions::OPTION_WAIT_SYNC => $this->getConnectionOption(
                                                                        ConnectionOptions::OPTION_WAIT_SYNC
                                ),
                       )
        );
 
        if (is_array($document)) {
            $document = Document::createFromArray($document);
        }
        $data = $document->getAll();
 
        $url = UrlHelper::appendParamsUrl(Urls::URL_DOCUMENT, $params);
 
        $response = $this->getConnection()->post($url, $this->json_encode_wrapper($data));
 
        $location = $response->getLocationHeader();
        if (!$location) {
            throw new ClientException('Did not find location header in server response');
        }
 
        $json = $response->getJson();
        $id   = UrlHelper::getDocumentIdFromLocation($location);
 
        $document->setInternalId($json[Document::ENTRY_ID]);
        $document->setRevision($json[Document::ENTRY_REV]);
 
        if ($id != $document->getId()) {
            throw new ClientException('Got an invalid response from the server');
        }
 
        $document->setIsNew(false);
 
        return $document->getId();
    }
 
 
    /**
     * Update an existing document in a collection, identified by the including _id and optionally _rev in the patch document.
     * Attention - The behavior of this method has changed since version 1.1
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the document to-be-replaced is the same as the one given.
     *
     * @throws Exception
     *
     * @param Document $document - The patch document that will update the document in question
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function update(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        return $this->updateById($collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Update an existing document in a collection, identified by collection id and document id
     * Attention - The behavior of this method has changed since version 1.1
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the document to-be-updated is the same as the one given.
     *
     * @throws Exception
     *
     * @param string    $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - patch document which contains the attributes and values to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function updateById($collectionId, $documentId, Document $document, $options = array())
    {
        return $this->patch(Urls::URL_DOCUMENT, $collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Update an existing document in a collection (internal method)
     *
     * @throws Exception
     *
     * @param string   $url          - server-side URL being called
     * @param string   $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - patch document which contains the attributes and values to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function patch($url, $collectionId, $documentId, Document $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_UPDATE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array(
                'waitForSync' => $this->getConnectionOption(ConnectionOptions::OPTION_WAIT_SYNC),
                'keepNull'    => true,
            )
        );
 
        $revision = $document->getRevision();
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $url    = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url    = UrlHelper::appendParamsUrl($url, $params);
        $result = $this->getConnection()->patch($url, $this->json_encode_wrapper($document->getAll()));
        $json   = $result->getJson();
        $document->setRevision($json[Document::ENTRY_REV]);
 
        return true;
    }
 
 
    /**
     * Replace an existing document in a collection, identified by the document itself
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the to-be-replaced document is the same as the one given.
     *
     * @throws Exception
     *
     * @param Document $document - document to be updated
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function replace(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        return $this->replaceById($collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Replace an existing document in a collection, identified by collection id and document id
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be Replaced
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the to-be-replaced document is the same as the one given.
     *
     * @throws Exception
     *
     * @param mixed    $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - document to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function replaceById($collectionId, $documentId, Document $document, $options = array())
    {
        return $this->put(Urls::URL_DOCUMENT, $collectionId, $documentId, $document, $options);
    }
   
    
    /**
     * Replace an existing document in a collection (internal method)
     *
     * @throws Exception
     *
     * @param string   $url          - the server-side URL being called
     * @param string   $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - document to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function put($url, $collectionId, $documentId, Document $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_REPLACE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array('waitForSync' => ConnectionOptions::OPTION_WAIT_SYNC)
        );
 
        $revision = $document->getRevision();
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $data   = $document->getAll();
        $url    = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url    = UrlHelper::appendParamsUrl($url, $params);
        $result = $this->getConnection()->put($url, $this->json_encode_wrapper($data));
        $json   = $result->getJson();
        $document->setRevision($json[Document::ENTRY_REV]);
 
        return true;
    }
 
    /**
     * Delete a document from a collection, identified by the document itself
     *
     * @throws Exception
     *
     * @param Document $document - document to be updated
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by remove()
     *
     */
    public function delete(Document $document, $options = array())
    {
        return $this->remove($document, $options);
    }
 
 
    /**
     * Remove a document from a collection, identified by the document itself
     *
     * @throws Exception
     *
     * @param Document $document - document to be removed
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function remove(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        $revision = $this->getRevision($document);
 
        return $this->deleteById($collectionId, $documentId, $revision, $options);
    }
 
 
    /**
     * Delete a document from a collection, identified by the collection id and document id
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param  mixed $revision     - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by removeById()
     */
    public function deleteById($collectionId, $documentId, $revision = null, $options = array())
    {
        $this->removeById($collectionId, $documentId, $revision, $options);
 
        return true;
    }
 
 
    /**
     * Remove a document from a collection, identified by the collection id and document id
     *
     * @throws Exception
     *
     * @param mixed  $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param  mixed $revision     - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function removeById($collectionId, $documentId, $revision = null, $options = array())
    {
       return $this->erase(Urls::URL_DOCUMENT, $collectionId, $documentId, $revision, $options);
    }
 
    
    /**
     * Remove a document from a collection (internal method)
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param mixed $revision      - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function erase($url, $collectionId, $documentId, $revision = null, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_DELETE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array('waitForSync' => ConnectionOptions::OPTION_WAIT_SYNC)
        );
 
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $url = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url = UrlHelper::appendParamsUrl($url, $params);
        $this->getConnection()->delete($url);
 
        return true;
    }
 
 
    /**
     * Helper function to get a document id from a document or a document id value
     *
     * @throws ClientException
     *
     * @param mixed $document - document id OR document to be updated
     *
     * @return mixed - document id, will throw if there is an error
     */
    private function getDocumentId($document)
    {
        if ($document instanceof Document) {
            $documentId = $document->getId();
        } else {
            $documentId = $document;
        }
 
        if (trim($documentId) === "" || !(is_string($documentId) || is_double($documentId) || is_int($documentId))) {
            throw new ClientException('Cannot alter a document without a document id');
        }
 
        return $documentId;
    }
 
 
    /**
     * Helper function to get a document id from a document or a document id value
     *
     * @throws ClientException
     *
     * @param mixed $document - document id OR document to be updated
     *
     * @return mixed - document id, will throw if there is an error
     */
    private function getRevision($document)
    {
        if ($document instanceof Document) {
            $revision = $document->getRevision();
        } else {
            $revision = null;
        }
 
        return $revision;
    }
 
 
    /**
     * Helper function to get a collection id from a document
     *
     * @throws ClientException
     *
     * @param Document $document - document id
     *
     * @return mixed - collection id, will throw if there is an error
     */
    private function getCollectionId(Document $document)
    {
        $collectionId = $document->getCollectionId();
 
        if (!$collectionId || !(is_string($collectionId) || is_double($collectionId) || is_int($collectionId))) {
            throw new ClientException('Cannot alter a document without a document id');
        }
 
        return $collectionId;
    }
}
#5triagens\ArangoDb\DocumentHandler->getById(shorten, RxQ4Pq17, Array())
/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/DocumentHandler.php (69)
<?php
 
/**
 * ArangoDB PHP client: document handler
 *
 * @package   triagens\ArangoDb
 * @author    Jan Steemann
 * @author    Frank Mayer
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */
 
namespace triagens\ArangoDb;
 
 
/**
 * A handler that manages documents
 *
 * A document handler that fetches documents from the server and
 * persists them on the server. It does so by issuing the
 * appropriate HTTP requests to the server.<br>
 *
 * <br>
 *
 * @package   triagens\ArangoDb
 * @since     0.2
 */
class DocumentHandler extends
    Handler
{
    /**
     * documents array index
     */
    const ENTRY_DOCUMENTS = 'documents';
 
    /**
     * collection parameter
     */
    const OPTION_COLLECTION = 'collection';
 
    /**
     * example parameter
     */
    const OPTION_EXAMPLE = 'example';
 
 
    /**
     * Get a single document from a collection
     *
     * Alias method for getById()
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'revision' - the documents revision</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    public function get($collectionId, $documentId, array $options = array())
    {
        return $this->getById($collectionId, $documentId, $options);
    }
 
 
    /**
     * Check if a document exists
     *
     * This will call self::get() internally and checks if there
     * was an exception thrown which represents an 404 request.
     *
     * @throws Exception When any other error than a 404 occurs
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed  $documentId   - document identifier
     * @return boolean
     */
    public function has($collectionId, $documentId)
    {
        try {
            // will throw ServerException if entry could not be retrieved
            $result = $this->get($collectionId, $documentId);
            return true;
        } catch (ServerException $e) {
            // we are expecting a 404 to return boolean false
            if ($e->getCode() === 404) {
                return false;
            }
 
            // just rethrow
            throw $e;
        }
 
        return false;
    }
 
 
    /**
     * Get a single document from a collection
     *
     * This will throw if the document cannot be fetched from the server.
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            <li>'revision' - The document is returned if it matches/not matches revision.</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    public function getById($collectionId, $documentId, array $options = array())
    {
        $data = $this->getDocument(Urls::URL_DOCUMENT, $collectionId, $documentId, $options);
        $options['_isNew'] = false;
 
        return $this->createFromArrayWithContext($data, $options);
    }
 
 
    /**
     * Get a single document (internal method)
     *
     * This method is the workhorse for getById() in this handler and the edges handler
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId   - document identifier
     * @param array $options      - optional, array of options
     *                            <p>Options are :
     *                            <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
     *                            <li>'includeInternals' - Deprecated, please use '_includeInternals'.</li>
     *                            <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
     *                            <li>'ignoreHiddenAttributes' - Deprecated, please use '_ignoreHiddenAttributes'.</li>
     *                            <li>'ifMatch' - boolean if given revision should match or not</li>
     *                            <li>'revision' - The document is returned if it matches/not matches revision.</li>
     *                            </p>
     *
     * @return Document - the document fetched from the server
     */
    protected function getDocument($url, $collectionId, $documentId, array $options = array())
    {
        $url      = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $headerElements = array();
        if (array_key_exists("ifMatch", $options) && array_key_exists("revision", $options)) {
            if ($options["ifMatch"] === true) {
                $headerElements["If-Match"] = '"' . $options["revision"] .'"';
            } else {
                $headerElements["If-None-Match"] = '"' . $options["revision"]. '"';
            }
        }
 
        $response = $this->getConnection()->get($url, $headerElements);
 
        if ($response->getHttpCode() === 304) {
            throw new ClientException('Document has not changed.');
        }
 
        return  $response->getJson();
    }
 
 
    /**
     * Gets information about a single documents from a collection
     *
     * This will throw if the document cannot be fetched from the server
     *
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as a string or number.
     * @param mixed $documentId   - document identifier.
     * @param boolean ifMatch     -  boolean if given revision should match or not.
     * @param string revision     - The document is returned if it matches/not matches revision.
     *
     * @return array - an array containing the complete header including the key httpCode.
     */
    public function getHead($collectionId, $documentId, $revision = null, $ifMatch = null)
    {
        return $this->head(Urls::URL_DOCUMENT, $collectionId, $documentId, $revision, $ifMatch);
    }
 
 
    /**
     * Get meta-data for a single document (internal method)
     *
     * This method is the workhorse for getHead() in this handler and the edges handler
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as a string or number
     * @param mixed $documentId    - document identifier
     * @param mixed $revision      - optional document revision
     * @param boolean ifMatch      -  boolean if given revision should match or not.
     *
     * @return array - the document meta-data
     */
    protected function head($url, $collectionId, $documentId, $revision = null, $ifMatch = null) {
        $url      = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $headerElements = array();
        if ($revision != null && $ifMatch !== null) {
            if ($ifMatch) {
                $headerElements["If-Match"] = '"' . $revision .'"';
            } else {
                $headerElements["If-None-Match"] = '"' . $revision . '"';
            }
        }
 
        $response = $this->getConnection()->head($url, $headerElements);
        $headers = $response->getHeaders();
        $headers["httpCode"] = $response->getHttpCode();
        return $headers;
    }
 
 
    /**
     * Intermediate function to call the createFromArray function from the right context
     *
     * @param $data
     * @param $options
     *
     * @return Document
     */
    protected function createFromArrayWithContext($data, $options)
    {
        return Document::createFromArray($data, $options);
    }
 
 
    /**
     * Get the list of all documents' ids from a collection
     *
     * This will throw if the list cannot be fetched from the server
     *
     * @throws Exception
     *
     * @param mixed $collectionId - collection id as string or number
     *
     * @return array - ids of documents in the collection
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by  CollectionHandler::getAllIds()
     *
     */
    public function getAllIds($collectionId)
    {
        $collectionHandler = new CollectionHandler($this->getConnection());
 
        return $collectionHandler->getAllIds($collectionId);
    }
 
 
    /**
     * Get document(s) by specifying an example
     *
     * This will throw if the list cannot be fetched from the server
     *
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param mixed      $document     - the example document as a Document object or an array
     * @param bool|array $options      - optional, prior to v1.0.0 this was a boolean value for sanitize, since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'_sanitize' - True to remove _id and _rev attributes from result documents. Defaults to false.</li>
     *                                 <li>'sanitize' - Deprecated, please use '_sanitize'.</li>
     *                                 <li>'_hiddenAttributes' - Set an array of hidden attributes for created documents.
     *                                 <li>'hiddenAttributes' - Deprecated, please use '_hiddenAttributes'.</li>
     *                                 <p>
     *                                 This is actually the same as setting hidden attributes using setHiddenAttributes() on a document. <br>
     *                                 The difference is, that if you're returning a resultset of documents, the getAll() is already called <br>
     *                                 and the hidden attributes would not be applied to the attributes.<br>
     *                                 </p>
     *                                 </li>
     *                                 <li>'batchSize' - can optionally be used to tell the server to limit the number of results to be transferred in one batch</li>
     *                                 <li>'skip' -  Optional, The number of documents to skip in the query.</li>
     *                                 <li>'limit' -  Optional, The maximal amount of documents to return. 'skip' is applied before the limit restriction.</li>
     *                                 </p>
     *
     * @return cursor - Returns a cursor containing the result
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by CollectionHandler::byExample()
     */
    public function getByExample($collectionId, $document, $options = false)
    {
        $collectionHandler = new CollectionHandler($this->getConnection());
 
        return $collectionHandler->byExample($collectionId, $document, $options);
    }
 
 
    /**
     * Add a document to a collection
     *
     * This will add the document to the collection and return the document's id
     *
     * This will throw if the document cannot be created
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param Document   $document     - the document to be added
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by save()
     *
     */
 
    public function add($collectionId, Document $document, $options = array())
    {
        return $this->save($collectionId, $document, $options);
    }
 
    /**
     * Store a document to a collection
     *
     * This is an alias/shortcut to save() and replace(). Instead of having to determine which of the 3 functions to use,
     * simply pass the document to store() and it will figure out which one to call.
     *
     * This will throw if the document cannot be saved or replaced.
     *
     * @throws Exception
     *
     * @param Document   $document     - the document to be added, can be passed as a document or an array
     * @param mixed      $collectionId - collection id as string or number
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.2.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     * @since 1.0
     */
    public function store(Document $document, $collectionId = null, $options = array())
    {
        if ($document->getIsNew()) {
 
            if ($collectionId == null) {
                throw new ClientException('A collection id is required to store a new document.');
            }
 
            $result = $this->save($collectionId, $document, $options);
            $document->setIsNew(false);
 
            return $result;
        } else {
 
            if ($collectionId) {
                throw new ClientException('An existing document cannot be stored into a new collection');
            }
 
            return $this->replace($document, $options);
        }
    }
 
 
    /**
     * save a document to a collection
     *
     * This will add the document to the collection and return the document's id
     *
     * This will throw if the document cannot be saved
     *
     * @throws Exception
     *
     * @param mixed      $collectionId - collection id as string or number
     * @param mixed      $document     - the document to be added, can be passed as a document or an array
     * @param bool|array $options      - optional, prior to v1.2.0 this was a boolean value for create. Since v1.0.0 it's an array of options.
     *                                 <p>Options are :<br>
     *                                 <li>'create' - create the collection if it does not yet exist.</li>
     *                                 <li>'waitForSync' -  if set to true, then all removal operations will instantly be synchronised to disk / If this is not specified, then the collection's default sync behavior will be applied.</li>
     *                                 </p>
     *
     * @return mixed - id of document created
     * @since 1.0
     */
    public function save($collectionId, $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        } 
        // This preserves compatibility for the old create parameter.
        $params = array(self::OPTION_COLLECTION => $collectionId);
        $params = $this->validateAndIncludeOldSingleParameterInParams(
                       $options,
                       $params,
                       ConnectionOptions::OPTION_CREATE
        );
 
        $params = $this->includeOptionsInParams(
                       $options,
                       $params,
                       array(
                            ConnectionOptions::OPTION_WAIT_SYNC => $this->getConnectionOption(
                                                                        ConnectionOptions::OPTION_WAIT_SYNC
                                ),
                       )
        );
 
        if (is_array($document)) {
            $document = Document::createFromArray($document);
        }
        $data = $document->getAll();
 
        $url = UrlHelper::appendParamsUrl(Urls::URL_DOCUMENT, $params);
 
        $response = $this->getConnection()->post($url, $this->json_encode_wrapper($data));
 
        $location = $response->getLocationHeader();
        if (!$location) {
            throw new ClientException('Did not find location header in server response');
        }
 
        $json = $response->getJson();
        $id   = UrlHelper::getDocumentIdFromLocation($location);
 
        $document->setInternalId($json[Document::ENTRY_ID]);
        $document->setRevision($json[Document::ENTRY_REV]);
 
        if ($id != $document->getId()) {
            throw new ClientException('Got an invalid response from the server');
        }
 
        $document->setIsNew(false);
 
        return $document->getId();
    }
 
 
    /**
     * Update an existing document in a collection, identified by the including _id and optionally _rev in the patch document.
     * Attention - The behavior of this method has changed since version 1.1
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the document to-be-replaced is the same as the one given.
     *
     * @throws Exception
     *
     * @param Document $document - The patch document that will update the document in question
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function update(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        return $this->updateById($collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Update an existing document in a collection, identified by collection id and document id
     * Attention - The behavior of this method has changed since version 1.1
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the document to-be-updated is the same as the one given.
     *
     * @throws Exception
     *
     * @param string    $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - patch document which contains the attributes and values to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function updateById($collectionId, $documentId, Document $document, $options = array())
    {
        return $this->patch(Urls::URL_DOCUMENT, $collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Update an existing document in a collection (internal method)
     *
     * @throws Exception
     *
     * @param string   $url          - server-side URL being called
     * @param string   $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - patch document which contains the attributes and values to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'keepNull' - can be used to instruct ArangoDB to delete existing attributes instead setting their values to null. Defaults to true (keep attributes when set to null)</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function patch($url, $collectionId, $documentId, Document $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_UPDATE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array(
                'waitForSync' => $this->getConnectionOption(ConnectionOptions::OPTION_WAIT_SYNC),
                'keepNull'    => true,
            )
        );
 
        $revision = $document->getRevision();
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $url    = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url    = UrlHelper::appendParamsUrl($url, $params);
        $result = $this->getConnection()->patch($url, $this->json_encode_wrapper($document->getAll()));
        $json   = $result->getJson();
        $document->setRevision($json[Document::ENTRY_REV]);
 
        return true;
    }
 
 
    /**
     * Replace an existing document in a collection, identified by the document itself
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be updated
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the to-be-replaced document is the same as the one given.
     *
     * @throws Exception
     *
     * @param Document $document - document to be updated
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document update operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function replace(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        return $this->replaceById($collectionId, $documentId, $document, $options);
    }
 
 
    /**
     * Replace an existing document in a collection, identified by collection id and document id
     *
     * This will update the document on the server
     *
     * This will throw if the document cannot be Replaced
     *
     * If policy is set to error (locally or globally through the ConnectionOptions)
     * and the passed document has a _rev value set, the database will check
     * that the revision of the to-be-replaced document is the same as the one given.
     *
     * @throws Exception
     *
     * @param mixed    $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - document to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function replaceById($collectionId, $documentId, Document $document, $options = array())
    {
        return $this->put(Urls::URL_DOCUMENT, $collectionId, $documentId, $document, $options);
    }
   
    
    /**
     * Replace an existing document in a collection (internal method)
     *
     * @throws Exception
     *
     * @param string   $url          - the server-side URL being called
     * @param string   $collectionId - collection id as string or number
     * @param mixed    $documentId   - document id as string or number
     * @param Document $document     - document to be updated
     * @param mixed    $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                               <p>Options are :
     *                               <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                               <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                               </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function put($url, $collectionId, $documentId, Document $document, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_REPLACE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array('waitForSync' => ConnectionOptions::OPTION_WAIT_SYNC)
        );
 
        $revision = $document->getRevision();
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $data   = $document->getAll();
        $url    = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url    = UrlHelper::appendParamsUrl($url, $params);
        $result = $this->getConnection()->put($url, $this->json_encode_wrapper($data));
        $json   = $result->getJson();
        $document->setRevision($json[Document::ENTRY_REV]);
 
        return true;
    }
 
    /**
     * Delete a document from a collection, identified by the document itself
     *
     * @throws Exception
     *
     * @param Document $document - document to be updated
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by remove()
     *
     */
    public function delete(Document $document, $options = array())
    {
        return $this->remove($document, $options);
    }
 
 
    /**
     * Remove a document from a collection, identified by the document itself
     *
     * @throws Exception
     *
     * @param Document $document - document to be removed
     * @param mixed    $options  - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                           <p>Options are :
     *                           <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                           <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                           </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function remove(Document $document, $options = array())
    {
        $collectionId = $this->getCollectionId($document);
        $documentId   = $this->getDocumentId($document);
 
        $revision = $this->getRevision($document);
 
        return $this->deleteById($collectionId, $documentId, $revision, $options);
    }
 
 
    /**
     * Delete a document from a collection, identified by the collection id and document id
     *
     * @throws Exception
     *
     * @param string $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param  mixed $revision     - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document replacement operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     *
     * @deprecated to be removed in version 2.0 - This function is being replaced by removeById()
     */
    public function deleteById($collectionId, $documentId, $revision = null, $options = array())
    {
        $this->removeById($collectionId, $documentId, $revision, $options);
 
        return true;
    }
 
 
    /**
     * Remove a document from a collection, identified by the collection id and document id
     *
     * @throws Exception
     *
     * @param mixed  $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param  mixed $revision     - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    public function removeById($collectionId, $documentId, $revision = null, $options = array())
    {
       return $this->erase(Urls::URL_DOCUMENT, $collectionId, $documentId, $revision, $options);
    }
 
    
    /**
     * Remove a document from a collection (internal method)
     *
     * @throws Exception
     *
     * @param string $url          - the server-side URL being called
     * @param string $collectionId - collection id as string or number
     * @param mixed  $documentId   - document id as string or number
     * @param mixed $revision      - optional revision of the document to be deleted
     * @param mixed  $options      - optional, array of options (see below) or the boolean value for $policy (for compatibility prior to version 1.1 of this method)
     *                             <p>Options are :
     *                             <li>'policy' - update policy to be used in case of conflict ('error', 'last' or NULL [use default])</li>
     *                             <li>'waitForSync' - can be used to force synchronisation of the document removal operation to disk even in case that the waitForSync flag had been disabled for the entire collection</li>
     *                             </p>
     *
     * @return bool - always true, will throw if there is an error
     */
    protected function erase($url, $collectionId, $documentId, $revision = null, $options = array())
    {
        if ($collectionId instanceof Collection) {
            $collectionId = $collectionId->getName();
        }
 
        // This preserves compatibility for the old policy parameter.
        $params = array();
        $params = $this->validateAndIncludeOldSingleParameterInParams(
            $options,
            $params,
            ConnectionOptions::OPTION_DELETE_POLICY
        );
        $params = $this->includeOptionsInParams(
            $options,
            $params,
            array('waitForSync' => ConnectionOptions::OPTION_WAIT_SYNC)
        );
 
        if (!is_null($revision)) {
            $params[ConnectionOptions::OPTION_REVISION] = $revision;
        }
 
        $url = UrlHelper::buildUrl($url, array($collectionId, $documentId));
        $url = UrlHelper::appendParamsUrl($url, $params);
        $this->getConnection()->delete($url);
 
        return true;
    }
 
 
    /**
     * Helper function to get a document id from a document or a document id value
     *
     * @throws ClientException
     *
     * @param mixed $document - document id OR document to be updated
     *
     * @return mixed - document id, will throw if there is an error
     */
    private function getDocumentId($document)
    {
        if ($document instanceof Document) {
            $documentId = $document->getId();
        } else {
            $documentId = $document;
        }
 
        if (trim($documentId) === "" || !(is_string($documentId) || is_double($documentId) || is_int($documentId))) {
            throw new ClientException('Cannot alter a document without a document id');
        }
 
        return $documentId;
    }
 
 
    /**
     * Helper function to get a document id from a document or a document id value
     *
     * @throws ClientException
     *
     * @param mixed $document - document id OR document to be updated
     *
     * @return mixed - document id, will throw if there is an error
     */
    private function getRevision($document)
    {
        if ($document instanceof Document) {
            $revision = $document->getRevision();
        } else {
            $revision = null;
        }
 
        return $revision;
    }
 
 
    /**
     * Helper function to get a collection id from a document
     *
     * @throws ClientException
     *
     * @param Document $document - document id
     *
     * @return mixed - collection id, will throw if there is an error
     */
    private function getCollectionId(Document $document)
    {
        $collectionId = $document->getCollectionId();
 
        if (!$collectionId || !(is_string($collectionId) || is_double($collectionId) || is_int($collectionId))) {
            throw new ClientException('Cannot alter a document without a document id');
        }
 
        return $collectionId;
    }
}
#6triagens\ArangoDb\DocumentHandler->get(shorten, RxQ4Pq17)
/var/www/html/app/routers/shorten.php (155)
<?php
use Phalcon\Http\Response;
use PhalconRest\Controllers\POSTController;
use Respect\Validation\Validator as v;
use PhalconRest\Models\Shorten;
use PhalconRest\Models\Flor;
use triagens\ArangoDb\Document as ArangoDocument;
use triagens\ArangoDb\CollectionHandler as ArangoCollectionHandler;
use triagens\ArangoDb\DocumentHandler as ArangoDocumentHandler;
use triagens\ArangoDb\Exception as ArangoException;
use triagens\ArangoDb\ConnectException as ArangoConnectException;
use triagens\ArangoDb\ClientException as ArangoClientException;
use triagens\ArangoDb\ServerException as ArangoServerException;
use triagens\ArangoDb\Statement as ArangoStatement;
 
$app->map('/shorten', function () use($app) {
    $response = new Response();
    $response->setContentType('application/json; charset=UTF-8');
    /***************************
     * METODO GET
     ***************************/
    if ( $app->request->isGet() ){
        $response->setStatusCode(200, "SUCCES");
        $response->setJsonContent(
            array(
                'status' => 'OK'
            )
        );
        return $response;
    }
    /***************************
     * METODO POST
     ***************************/
    if ( $app->request->isPost() ){
        // Creo instancia de un controlador tipo post
        $post = new PostController($di = $app->request);
        // Obtengo las variables dependiendo de que tipo de http sea.
        $data = $post->SetFields();
        if ( isset($data['error']) || count($data) == 0 ) {
            $response->setStatusCode(400, "BAD REQUEST");
            $response->setJsonContent(array(
                'status' => 'error',
                'code' => 403,
                'message' => 'Envio de informacion no valida'
            ));
            return $response;
        }
        // Si todo esta bien  paso a guardar la url
        if ( v::notEmpty()->url()->validate($data['url']) ) {
            // Nueva instancia en la base de datos.
            $ShortentModel = new Shorten;
            // Agregar el parametro de {url} que viene por post.
            $ShortentModel->link_destino = $data['url'];
            // Salvo el dato en la base de datos relacional.
            if ( $ShortentModel->save() ) {
                /**
                 * Si existe la variable custom el _KEY tomara el valor de esa variable,
                 * caso contrario se generara el  _KEY basado en el ID del elemento.
                 * @var string
                 */
                $shorten = isset($data['custom'])  ? $data['custom'] :  $app->hashids->encode($ShortentModel->shorten_id);
                /**
                 * ARANGO DB
                 */
                // Guardo los documentos en arangodb
                try {
                    // Extraigo Metadatos de la url
                    //$urlExtractor = new \rollbackpt\UrlExtractor\UrlExtractor();
                    //$Metatags = $urlExtractor->extractAll($data['url'],false);
                    // Creo el controlador
                    $handler = new ArangoDocumentHandler($app['arango']);
                    // Creo un nuevo documento
                    $shorcutArango = new ArangoDocument();
                    $shorcutArango->set('dest', $data['url']);
                    $shorcutArango->set('shorten','http://thoy.mx/'.$shorten);
                    $shorcutArango->set('code',$shorten);
                    $shorcutArango->set('clicks',0);
                    $shorcutArango->set('title',"Prueba-2016-06-08");
                    $shorcutArango->set('created_at',$ShortentModel->created_at);
                    $shorcutArango->set('_key',$shorten);
                    // send the document to the server
                    $id = $handler->add('shorten', $shorcutArango);
                    // Retorno los datos del shorten
                    $response->setStatusCode(200, "SUCCES");
                    $response->setJsonContent(
                        array(
                            'status' =>'success',
                            'code' => 200,
                            'shorten_code' => $shorten,
                            'shorten_link' => 'http://thoy.mx/'.$shorten,
                            'shorten_qr' => 'http://thoy.mx/'.$shorten."-qr"
                        )
                    );
                } catch (ArangoConnectException $e) {
                  $response->setJsonContent(array(
                      "code" => 48,
                      "status" => "error",
                      "errors" => $e->getMessage(),
                  ));
                  return $response;
                } catch (ArangoClientException $e) {
                  $response->setJsonContent(array(
                      "code" => 48,
                      "status" => "error",
                      "errors" => $e->getMessage(),
                  ));
                } catch (ArangoServerException $e) {
                  $response->setJsonContent(array(
                      "code" => $e->getServerCode(),
                      "status" => "error",
                      "errors" => $e->getMessage(),
                      "server" =>  $e->getServerMessage()
                  ));
                }
            }
            else{
                $response->setStatusCode(400, "BAD REQUEST");
                $Message = array();
                foreach ( $ShortentModel->getMessages() as $key => $message) {
                    $Message[$key] = array(
                        'field' => $message->getField() ,
                        'message' => $message->getMessage()
                    );
                }
                $response->setJsonContent(array(
                    "code" => 48,
                    "status" => "error",
                    "errors" => $Message,
                ));
                return $response;
            }
        }
        // Retorno error
        else{
            $response->setJsonContent(
                array(
                    'status' =>'error',
                    'mensaje' =>'La url no es valida.'
                )
            );
        }
        return $response;
    }
})->setName('shorten');
/**
 * Url que añade una visita al shorten es la url mas importante.
 * @var [type]
 * @return redireccionamiento a la url
 */
$app->get('/{id:[a-zA-Z0-9_-]+}', function ($shorten) use($app) {
    $handler = new ArangoDocumentHandler($app['arango']);
    $response = new Response();
    try {
 
        $DataShorten = $handler->get('shorten', str_replace("-qr","",$shorten) );
        /* Variables de comprobacion pasadas al AQL*/
        $Title = $DataShorten->title;
        $QR = "";
        $url = $app->request->getHTTPReferer();
 
        /*Caso especial de Twitter*/
        if (strpos($url, 't.co') !== false) {
            $url= "www.twitter.com";
        }
        /*Caso especial de Facebook L*/
        if (strpos($url, 'l.facebook.com') !== false || strpos($url, 'lm.facebook.com') !== false) {
            $url= "http://l.facebook.com/";
        }
        /*Caso especial de Facebook M*/
        if (strpos($url, 'm.facebook.com') !== false ) {
            $url= "http://m.facebook.com/";
        }
        /* Si no tiene referencia. */
        if ($url == "") {
            $url = "Direct";
        }
        /** Si no existe titulo **/
        if ($DataShorten->title == "") {
            $Title = "Prueba-2016-06-08";
        }
        /** Agregar Si el valor viene  de un QR **/
        $qr = strstr($shorten, '-');
        $QR= null;
        if ($qr) {
            $QR = 1;
        }
 
        /** Consulta AQL **/
        $AQL = new ArangoStatement($app['arango'], array(
                'query' =>'FOR n IN shorten FILTER n._key == @key LET NewClick =  (! TO_NUMBER(@qr) ? n.clicks + 1 : n.clicks ) LET Title = (! LENGTH(@title) ? n.title : @title ) LET Nqr = (! TO_NUMBER(@qr) ? TO_NUMBER(n.qr) : n.qr + 1 ) LET Stats = ( n.stats ? n.stats : [] ) LET URL = @url LET CurrentList = ( FOR element IN Stats FILTER element.url == @url LIMIT 1 RETURN element ) LET NuevaList = (! (LENGTH(CurrentList) > 0) ? PUSH(Stats,{ url: @url , visitas: 1 }) : (FOR element IN Stats LET NewItem = (! POSITION( TO_ARRAY(element) , @url ) ? element : MERGE(element, { url: @url , visitas: (TO_NUMBER(element.visitas) + 1)})) RETURN NewItem ) ) UPDATE n WITH { stats:  NuevaList, clicks : NewClick, title: Title, qr:Nqr  } IN shorten',
                'bindVars' => array(
                    'url' => $url ,
                    'qr' => $QR,
                    'title' => $Title,
                    'key' => $DataShorten->code
                )
        ));
        $AQL->execute();
        /** Redirect **/
        $response->redirect($DataShorten->dest, false, 200);
        return $response;
    } catch (ArangoServerException  $e) {
 
         $response->redirect("http://www.tabascohoy.com/", false, 200);
         return $response;
    }
})->setName('getshorten');
 
 
 
 
 
$app->get('/partidos', function () use($app) {
    $handler = new ArangoDocumentHandler($app['arango']);
    $partidos = $handler->getByExample('partidos',array('activo' => true));
    $items = array();
    $response = new Response();
    $response->setContentType('application/json; charset=UTF-8');
 
    foreach ($partidos->getAll() as $key => $value) {
        array_push($items, array(
                'info' => $value->info,
                'fecha' => $value->fecha,
                'estadio' => $value->estadio,
                'equipo_a' => $value->equipo_a,
                'equipo_b' => $value->equipo_b,
                'order' => $value->order
 
            )
        );
    }
    return $items;
 
});
 
 
$app->post('/eligetuflor', function () use($app) {
    $response = new Response();
    $handler = new ArangoDocumentHandler($app['arango']);
    $headers = $app->request->getHeaders();
    $now = new DateTime();
    $datos =  [
        "nombre" => $app->request->getPost('nombre'),
        "paterno" => $app->request->getPost('paterno'),
        "materno" => $app->request->getPost('materno'),
        "email" => $app->request->getPost('email'),
        "direccion" => $app->request->getPost('direccion'),
        "telefono" => $app->request->getPost('telefono'),
        "municipio" => $app->request->getPost('municipio'),
        "flor0" => $app->request->getPost('flor0'),
        "flor1" => $app->request->getPost('flor1'),
        "flor2" => $app->request->getPost('flor2'),
        "flor3" => $app->request->getPost('flor3'),
        "flor4" => $app->request->getPost('flor4')
    ];
    // Creo un nuevo documento
    /*
    $Flor = new ArangoDocument();
    $Flor->set('nombre', $datos['nombre']);
    $Flor->set('paterno', $datos['paterno']);
    $Flor->set('materno', $datos['materno']);
    $Flor->set('email', $datos['email']);
    $Flor->set('direccion', $datos['direccion']);
    $Flor->set('telefono', $datos['telefono']);
    $Flor->set('municipio', $datos['municipio']);
    $Flor->set('flor0', $datos['flor0']);
    $Flor->set('flor1', $datos['flor1']);
    $Flor->set('flor2', $datos['flor2']);
    $Flor->set('flor3', $datos['flor3']);
    $Flor->set('flor4', $datos['flor4']);
    $Flor->set('created_at', date("Y-m-d H:i:s"));
    $Flor->set('code', $app->hashids->encode($now->getTimestamp()));
    // send the document to the server
    $resultPost = $handler->add('flortabasco', $Flor);
    // Retorno los datos del shorten
    */
    $response->setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, HEAD');
    $response->setHeader('Access-Control-Allow-Origin', '*');
    $response->setHeader('Access-Control-Allow-Credentials', 'true');
    $response->setHeader('Access-Control-Allow-Headers', "origin, x-requested-with, content-type");
    $FlorModel = new Flor;
    if ($FlorModel->save($datos) === false) {
        $response->setStatusCode(400, "BAD REQUEST");
        $Message = array();
        foreach ( $FlorModel->getMessages() as $key => $message) {
            $Message[$key] = array(
                'field' => $message->getField() ,
                'message' => $message->getMessage()
            );
        }
        $response->setJsonContent([
            "code" => 48,
            "status" => "error",
            "errors" => $Message,
        ]);
        return $response;
    } else {
        //$FlorModel->save($datos);
        $codigo = $app->hashids->encode($FlorModel->id);
        /*
        $mail = new PHPMailer;
        $mail->isSMTP();                                      // Set mailer to use SMTP
        $mail->Host = 'mail.tabascohoy.com';  // Specify main and backup SMTP servers
        $mail->SMTPAuth = true;
        $mail->SMTPSecure = false;
        $mail->SMTPAutoTLS = false;                               // Enable SMTP authentication
        $mail->Username = 'no-reply@tabascohoy.com';                 // SMTP username
        $mail->Password = 'l9o1dZ_6l9o1dZ_6';                           // SMTP password
        //$mail->SMTPSecure = 'STARTTLS';                            // Enable TLS encryption, `ssl` also accepted
        $mail->Port = 587;                                    // TCP port to connect to
 
        $mail->setFrom('no-reply@tabascohoy.com', 'Tabasco HOY');
        $mail->addAddress($FlorModel->email);     // Add a recipient
        //$mail->addAddress('ellen@example.com');               // Name is optional
        //$mail->addReplyTo('info@example.com', 'Information');
        //$mail->addCC('cc@example.com');
        //$mail->addBCC('bcc@example.com');
 
        //$mail->addAttachment('/var/tmp/file.tar.gz');         // Add attachments
        //$mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name
        $mail->isHTML(true);                                  // Set email format to HTML
 
        $mail->Subject = 'Ya estás participando, mantente pendiente de nuestras redes sociales.';
        $mail->Body    = '
                <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
                <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
                <head>
                    <link rel="stylesheet" type="text/css" href="http://www.tabascohoy.com/mail/css/app.css">
                    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
                    <meta name="viewport" content="width=device-width">
                    <title>Elige tu Flor 2017 - Tabasco Hoy y Grupo Cantón</title>
                    <!-- <style> -->
                </head>
                <body>
                    <span class="preheader"></span>
                    <table class="body">
                    <tr>
                        <td class="center" align="center" valign="top">
                        <center data-parsed="">
                            <table align="center" class="wrapper header float-center"><tr><td class="wrapper-inner">
                            <table align="center" class="container"><tbody><tr><td>
                                <table class="row collapse"><tbody><tr>
                                <th class="small-6 large-6 columns first"><table><tr><th>
                                    <img src="http://www.tabascohoy.com/mail/assets/img/eligetuflor_logo.png" alt="Elige tu Flor 2017">
                                </th></tr></table></th>
                                <th class="small-6 large-6 columns last"><table><tr><th>
                                    <img src="http://www.tabascohoy.com/mail/assets/img/tabascohoy_logo.png" alt="Tabasco Hoy" class="right">
                                </th></tr></table></th>
                                </tr></tbody></table>
                            </td></tr></tbody></table>
                            </td></tr></table>
                            <table align="center" class="container float-center"><tbody><tr><td>
                            <table class="row"><tbody><tr>
                                <th class="folio small-12 large-12 columns first last"><table><tr><th>
                                <h1><span class="small">Este es tu número de folio</span></h1>
                                <h1 class="lead">'.$codigo.'</h1>
                                </th>
                <th class="expander"></th></tr></table></th>
                            </tr></tbody></table>
                            <table class="row"><tbody><tr>
                                <th class="maintext small-12 large-12 columns first last"><table><tr><th>
                                <h2>Gracias por tu participación.</h2>
                                <p>Asegúrate de conservar este folio para acreditar tu premio<br>en caso de resultar ganador</p>
                                <h3>¡Mucha suerte!</h3>
                                <p><small><a href:"http:="" tabascohoy.com="" eligetuflor"="">Haz click aquí para consultar las bases del concurso</a></small></p>
                                </th>
                <th class="expander"></th></tr></table></th>
                            </tr></tbody></table>
                            </td></tr></tbody></table>
                            <table align="center" class="wrapper header secondary float-center"><tr><td class="wrapper-inner">
                            <table align="center" class="container"><tbody><tr><td>
                                <table class="row collapse"><tbody><tr>
                                <th class="vertical small-6 large-6 columns first"><table><tr><th>
                                    <img alt="Grupo Cantón" src="http://www.tabascohoy.com/mail/assets/img/grupocanton_logo.png">
                                </th></tr></table></th>
                                <th class="vertical small-6 large-6 columns last"><table><tr><th>
                                    <p>SÍGUENOS</p> <a href="https://www.facebook.com/TabHoy/"><img src="http://www.tabascohoy.com/mail/assets/img/f_icon.png" alt="Facebook TabascoHoy"></a><a href="https://twitter.com/tabascohoy"><img src="http://www.tabascohoy.com/mail/assets/img/birb_icon.png" alt="Twitter Tabasco Hoy"> </a><a href="https://www.instagram.com/tabascohoy/"><img src="http://www.tabascohoy.com/mail/assets/img/insta_icon.png" alt="Instagram Tabasco Hoy"> </a><a href="https://www.youtube.com/channel/UChBDAWf9KvXnKQc7pOKVTVA"><img src="http://www.tabascohoy.com/mail/assets/img/you_icon.png" alt="Youtube Tabasco Hoy"> </a>
                                </th></tr></table></th>
                                </tr></tbody></table>
                            </td></tr></tbody></table>
                            </td></tr></table>
                        </center>
                        </td>
                    </tr>
                    </table>
                    <!-- prevent Gmail on iOS font size manipulation -->
                <div style="display:none; white-space:nowrap; font:15px courier; line-height:0;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>
                </body>
                </html>
        ';
        if(!$mail->send()) {
            echo 'Message could not be sent.';
            echo 'Mailer Error: ' . $mail->ErrorInfo;
            exit();
            $response->setJsonContent(
                array(
                    'status' =>'0k',
                    'codigo' =>  $mail->ErrorInfo,
 
                )
            );
            $response->setStatusCode(404, "BAD REQUEST");
        } else {
            $response->setJsonContent(
                array(
                    'status' =>'0k',
                    'codigo' =>  $codigo,
                    'datos' =>  $datos
                )
            );
            $response->setStatusCode(200, "SUCCES");
        }
        */
        $response->setJsonContent(
            array(
                'status' =>'0k',
                'codigo' =>  $codigo,
                'datos' =>  $datos
            )
        );
        return $response;
    }
    //$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
})->setName('eligetuflor');
#7Closure->{closure}(RxQ4Pq17)
#8Phalcon\Mvc\Micro->handle()
/var/www/html/public/index.php (51)
<?php
error_reporting(E_ALL);
use Phalcon\Mvc\Micro;
define('APP_PATH', realpath('..'));
 
 
try {
 
    /**
     * Read the configuration
     */
    $config = include APP_PATH . "/app/config/config.php";
 
    /**
     * Include Autoloader
     */
    include APP_PATH . '/app/config/loader.php';
    /**
     * Include Services
     */
    include APP_PATH . '/app/config/services.php';
    /**
     * Add Events Manager.
     */
    include APP_PATH.'/app/config/manager.php';
    /**
     * Starting the application
     * Assign service locator to the application
     */
    $app = new Micro($di);
    $app->setEventsManager($eventsManager);
    /**
     * Mount all of the collections, which makes the routes active.
     */
    foreach($di->get('collections') as $collection){
        $app->mount($collection);
    }
 
    /**
     * Include Application
     */
    include APP_PATH . '/app/app.php';
    /**
     * Handle the request
     */
    $app->handle();
 
} catch (\Exception $e) {
    $debug = new \Phalcon\Debug();
    $debug->listen();
    echo $app->handle()->getContent();
}
KeyValue
_url/RxQ4Pq17
KeyValue
USERwww-data
HOME/var/www
FCGI_ROLERESPONDER
PATH_INFO
PATH_TRANSLATED/var/www/html/public
SCRIPT_FILENAME/var/www/html/public/index.php
QUERY_STRING_url=/RxQ4Pq17&
REQUEST_METHODGET
CONTENT_TYPE
CONTENT_LENGTH
SCRIPT_NAME/index.php
REQUEST_URI/RxQ4Pq17
DOCUMENT_URI/index.php
DOCUMENT_ROOT/var/www/html/public
SERVER_PROTOCOLHTTP/1.1
REQUEST_SCHEMEhttp
GATEWAY_INTERFACECGI/1.1
SERVER_SOFTWAREnginx/1.9.3
REMOTE_ADDR35.175.113.29
REMOTE_PORT50364
SERVER_ADDR45.55.53.44
SERVER_PORT80
SERVER_NAMEthoy.mx
REDIRECT_STATUS200
HTTP_USER_AGENTCCBot/2.0 (https://commoncrawl.org/faq/)
HTTP_ACCEPTtext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_LANGUAGEen-US,en;q=0.5
HTTP_ACCEPT_ENCODINGbr,gzip
HTTP_HOSTthoy.mx
HTTP_CONNECTIONKeep-Alive
PHP_SELF/index.php
REQUEST_TIME_FLOAT1582339815.9247
REQUEST_TIME1582339815
#Path
0/var/www/html/public/index.php
1/var/www/html/app/config/config.php
2/var/www/html/app/config/loader.php
3/var/www/html/vendor/autoload.php
4/var/www/html/vendor/composer/autoload_real.php
5/var/www/html/vendor/composer/ClassLoader.php
6/var/www/html/vendor/composer/autoload_namespaces.php
7/var/www/html/vendor/composer/autoload_psr4.php
8/var/www/html/vendor/composer/autoload_classmap.php
9/var/www/html/app/config/services.php
10/var/www/html/app/config/manager.php
11/var/www/html/app/routers/routeLoader.php
12/var/www/html/app/routers/collections/UserCollections.php
13/var/www/html/app/controllers/UsersController.php
14/var/www/html/app/controllers/RESTController.php
15/var/www/html/app/controllers/BaseController.php
16/var/www/html/app/routers/collections/example.php
17/var/www/html/app/app.php
18/var/www/html/app/routers/shorten.php
19/var/www/html/app/routers/webhook.php
20/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/DocumentHandler.php
21/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Handler.php
22/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/ConnectionOptions.php
23/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/UpdatePolicy.php
24/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Connection.php
25/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/DefaultValues.php
26/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Endpoint.php
27/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Urls.php
28/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/UrlHelper.php
29/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/HttpHelper.php
30/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/ConnectException.php
31/var/www/html/vendor/triagens/arangodb/lib/triagens/ArangoDb/Exception.php
Memory
Usage1048576