Phalcon Framework 2.0.9

Elasticsearch\Common\Exceptions\Missing404Exception: {"error":"IndexMissingException[[partsit] missing]","status":404}

/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php (564)
#0Elasticsearch\Connections\Connection->process4xxError(Array([http_method] => GET, [scheme] => https, [uri] => /partsit/parts/_search, [body] => {"query":{"query_string":{"query":"*","default_operator":"and"}},"filter":{"and":[{"bool":{"must":{"term":{"sectionID":4}}}},{"bool":{"must":{"term":{"categoryID":57}}}},{"bool":{"must":{"term":{"subCategoryID":251}}}}]}}, [headers] => Array([host] => Array([0] => search-partsit-amts77ss2mcide4iwmj4hu6f6e.eu-west-1.es.amazonaws.com:443))), Array([transfer_stats] => Array(28), [curl] => Array([error] => (empty string), [errno] => 0), [effective_url] => https://search-partsit-amts77ss2mcide4iwmj4hu6f6e.eu-west-1.es.amazonaws.com:443/partsit/parts/_search, [headers] => Array([Access-Control-Allow-Origin] => Array([0] => *), [Content-Type] => Array([0] => application/json; charset=UTF-8), [Content-Length] => Array([0] => 65), [Connection] => Array([0] => keep-alive)), [version] => 1.1, [status] => 404, [reason] => Not Found, [body] => {"error":"IndexMissingException[[partsit] missing]","status":404}), Array())
/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php (262)
<?php
 
namespace Elasticsearch\Connections;
 
use Elasticsearch\Common\Exceptions\AlreadyExpiredException;
use Elasticsearch\Common\Exceptions\BadRequest400Exception;
use Elasticsearch\Common\Exceptions\Conflict409Exception;
use Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost;
use Elasticsearch\Common\Exceptions\Curl\CouldNotResolveHostException;
use Elasticsearch\Common\Exceptions\Curl\OperationTimeoutException;
use Elasticsearch\Common\Exceptions\Forbidden403Exception;
use Elasticsearch\Common\Exceptions\MaxRetriesException;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use Elasticsearch\Common\Exceptions\NoDocumentsToGetException;
use Elasticsearch\Common\Exceptions\NoShardAvailableException;
use Elasticsearch\Common\Exceptions\RequestTimeout408Exception;
use Elasticsearch\Common\Exceptions\RoutingMissingException;
use Elasticsearch\Common\Exceptions\ScriptLangNotSupportedException;
use Elasticsearch\Common\Exceptions\ServerErrorResponseException;
use Elasticsearch\Common\Exceptions\TransportException;
use Elasticsearch\Serializers\SerializerInterface;
use Elasticsearch\Transport;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Exception\ConnectException;
use GuzzleHttp\Ring\Exception\RingException;
use Psr\Log\LoggerInterface;
 
/**
 * Class AbstractConnection
 *
 * @category Elasticsearch
 * @package  Elasticsearch\Connections
 * @author   Zachary Tong <zachary.tong@elasticsearch.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
 * @link     http://elasticsearch.org
 */
class Connection implements ConnectionInterface
{
    /** @var  callable */
    protected $handler;
 
    /** @var SerializerInterface */
    protected $serializer;
 
    /**
     * @var string
     */
    protected $transportSchema = 'http';    // TODO depreciate this default
 
    /**
     * @var string
     */
    protected $host;
 
    /**
     * @var string || null
     */
    protected $path;
 
    /**
     * @var LoggerInterface
     */
    protected $log;
 
    /**
     * @var LoggerInterface
     */
    protected $trace;
 
    /**
     * @var array
     */
    protected $connectionParams;
 
    /** @var bool  */
    protected $isAlive = false;
 
    /** @var float  */
    private $pingTimeout = 1;    //TODO expose this
 
    /** @var int  */
    private $lastPing = 0;
 
    /** @var int  */
    private $failedPings = 0;
 
    private $lastRequest = array();
 
    /**
     * Constructor
     *
     * @param $handler
     * @param array $hostDetails
     * @param array $connectionParams Array of connection-specific parameters
     * @param \Elasticsearch\Serializers\SerializerInterface $serializer
     * @param \Psr\Log\LoggerInterface $log              Logger object
     * @param \Psr\Log\LoggerInterface $trace
     */
    public function __construct($handler, $hostDetails, $connectionParams,
                                SerializerInterface $serializer, LoggerInterface $log, LoggerInterface $trace)
    {
        if (isset($hostDetails['port']) !== true) {
            $hostDetails['port'] = 9200;
        }
 
        if (isset($hostDetails['scheme'])) {
            $this->transportSchema = $hostDetails['scheme'];
        }
 
        if (isset($hostDetails['user']) && isset($hostDetails['pass'])) {
            $connectionParams['client']['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
            $connectionParams['client']['curl'][CURLOPT_USERPWD] = $hostDetails['user'].':'.$hostDetails['pass'];
        }
 
        $host = $hostDetails['host'].':'.$hostDetails['port'];
        $path = null;
        if (isset($hostDetails['path']) === true) {
            $path = $hostDetails['path'];
        }
        $this->host             = $host;
        $this->path             = $path;
        $this->log              = $log;
        $this->trace            = $trace;
        $this->connectionParams = $connectionParams;
        $this->serializer       = $serializer;
 
        $this->handler = $this->wrapHandler($handler, $log, $trace);
    }
 
    /**
     * @param $method
     * @param $uri
     * @param null $params
     * @param null $body
     * @param array $options
     * @param \Elasticsearch\Transport $transport
     * @return mixed
     */
    public function performRequest($method, $uri, $params = null, $body = null, $options = [], Transport $transport = null)
    {
        if (isset($body) === true) {
            $body = $this->serializer->serialize($body);
        }
 
        $request = [
            'http_method' => $method,
            'scheme'      => $this->transportSchema,
            'uri'         => $this->getURI($uri, $params),
            'body'        => $body,
            'headers'     => [
                'host'  => [$this->host]
            ]
 
        ];
        $request = array_merge_recursive($request, $this->connectionParams, $options);
 
 
        $handler = $this->handler;
        $future = $handler($request, $this, $transport, $options);
 
        return $future;
    }
 
    /** @return string */
    public function getTransportSchema()
    {
        return $this->transportSchema;
    }
 
    /** @return array */
    public function getLastRequestInfo()
    {
        return $this->lastRequest;
    }
 
    private function wrapHandler(callable $handler, LoggerInterface $logger, LoggerInterface $tracer)
    {
        return function (array $request, Connection $connection, Transport $transport = null, $options) use ($handler, $logger, $tracer) {
 
            $this->lastRequest = [];
            $this->lastRequest['request'] = $request;
 
            // Send the request using the wrapped handler.
            $response =  Core::proxy($handler($request), function ($response) use ($connection, $transport, $logger, $tracer, $request, $options) {
 
                $this->lastRequest['response'] = $response;
 
                if (isset($response['error']) === true) {
                    if ($response['error'] instanceof ConnectException || $response['error'] instanceof RingException) {
 
                        $this->log->warning("Curl exception encountered.");
 
                        $exception = $this->getCurlRetryException($request, $response);
 
                        $this->logRequestFail(
                            $request['http_method'],
                            $response['effective_url'],
                            $request['body'],
                            $request['headers'],
                            $response['status'],
                            $response['body'],
                            $response['transfer_stats']['total_time'],
                            $exception
                        );
 
                        $node = $connection->getHost();
                        $this->log->warning("Marking node $node dead.");
                        $connection->markDead();
 
                        // If the transport has not been set, we are inside a Ping or Sniff,
                        // so we don't want to retrigger retries anyway.
                        //
                        // TODO this could be handled better, but we are limited because connectionpools do not
                        // have access to Transport.  Architecturally, all of this needs to be refactored
                        if (isset($transport) === true) {
                            $transport->connectionPool->scheduleCheck();
 
                            $neverRetry = isset($request['client']['never_retry']) ? $request['client']['never_retry'] : false;
                            $shouldRetry = $transport->shouldRetry($request);
                            $shouldRetryText = ($shouldRetry) ? 'true' : 'false';
 
                            $this->log->warning("Retries left? $shouldRetryText");
                            if ($shouldRetry && !$neverRetry) {
                                return $transport->performRequest(
                                    $request['http_method'],
                                    $request['uri'],
                                    [],
                                    $request['body'],
                                    $options
                                );
                            }
                        }
 
                        $this->log->warning("Out of retries, throwing exception from $node");
                        // Only throw if we run out of retries
                        throw $exception;
                    } else {
                        // Something went seriously wrong, bail
                        $exception = new TransportException($response['error']->getMessage());
                        $this->logRequestFail(
                            $request['http_method'],
                            $response['effective_url'],
                            $request['body'],
                            $request['headers'],
                            $response['status'],
                            $response['body'],
                            $response['transfer_stats']['total_time'],
                            $exception
                        );
                        throw $exception;
                    }
                } else {
                    $connection->markAlive();
 
                    if (isset($response['body']) === true) {
                        $response['body'] = stream_get_contents($response['body']);
                        $this->lastRequest['response']['body'] = $response['body'];
                    }
 
                    if ($response['status'] >= 400 && $response['status'] < 500) {
                        $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : [];
                        $this->process4xxError($request, $response, $ignore);
                    } elseif ($response['status'] >= 500) {
                        $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : [];
                        $this->process5xxError($request, $response, $ignore);
                    }
 
                    // No error, deserialize
                    $response['body'] = $this->serializer->deserialize($response['body'], $response['transfer_stats']);
                }
                $this->logRequestSuccess(
                    $request['http_method'],
                    $response['effective_url'],
                    $request['body'],
                    $request['headers'],
                    $response['status'],
                    $response['body'],
                    $response['transfer_stats']['total_time']
                );
 
                return isset($request['client']['verbose']) && $request['client']['verbose'] === true ? $response : $response['body'];
 
            });
 
            return $response;
        };
    }
 
    /**
     * @param string $uri
     * @param array $params
     *
     * @return string
     */
    private function getURI($uri, $params)
    {
        if (isset($params) === true && !empty($params)) {
            $uri .= '?' . http_build_query($params);
        }
 
        if ($this->path !== null) {
            $uri = $this->path . $uri;
        }
 
        return $uri;
    }
 
    /**
     * Log a successful request
     *
     * @param string $method
     * @param string $fullURI
     * @param string $body
     * @param array  $headers
     * @param string $statusCode
     * @param string $response
     * @param string $duration
     *
     * @return void
     */
    public function logRequestSuccess($method, $fullURI, $body, $headers, $statusCode, $response, $duration)
    {
        $this->log->debug('Request Body', array($body));
        $this->log->info(
            'Request Success:',
            array(
                'method'    => $method,
                'uri'       => $fullURI,
                'headers'   => $headers,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
        $this->log->debug('Response', array($response));
 
        // Build the curl command for Trace.
        $curlCommand = $this->buildCurlCommand($method, $fullURI, $body);
        $this->trace->info($curlCommand);
        $this->trace->debug(
            'Response:',
            array(
                'response'  => $response,
                'method'    => $method,
                'uri'       => $fullURI,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
    }
 
    /**
     * Log a a failed request
     *
     * @param string $method
     * @param string $fullURI
     * @param string $body
     * @param array $headers
     * @param null|string $statusCode
     * @param null|string $response
     * @param string $duration
     * @param \Exception|null $exception
     *
     * @return void
     */
    public function logRequestFail($method, $fullURI, $body, $headers, $statusCode, $response, $duration, \Exception $exception)
    {
        $this->log->debug('Request Body', array($body));
        $this->log->warning(
            'Request Failure:',
            array(
                'method'    => $method,
                'uri'       => $fullURI,
                'headers'   => $headers,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
                'error'     => $exception->getMessage(),
            )
        );
        $this->log->warning('Response', array($response));
 
        // Build the curl command for Trace.
        $curlCommand = $this->buildCurlCommand($method, $fullURI, $body);
        $this->trace->info($curlCommand);
        $this->trace->debug(
            'Response:',
            array(
                'response'  => $response,
                'method'    => $method,
                'uri'       => $fullURI,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
    }
 
    /**
     * @return bool
     */
    public function ping()
    {
        $options = [
            'client' => [
                'timeout' => $this->pingTimeout,
                'never_retry' => true,
                'verbose' => true
            ]
        ];
        try {
            $response = $this->performRequest('HEAD', '/', null, null, $options);
            $response = $response->wait();
        } catch (TransportException $exception) {
            $this->markDead();
 
            return false;
        }
 
        if ($response['status'] === 200) {
            $this->markAlive();
 
            return true;
        } else {
            $this->markDead();
 
            return false;
        }
    }
 
    /**
     * @return array
     */
    public function sniff()
    {
        $options = [
            'client' => [
                'timeout' => $this->pingTimeout,
                'never_retry' => true
            ]
        ];
 
        return $this->performRequest('GET', '/_nodes/_all/clear', null, null, $options);
    }
 
    /**
     * @return bool
     */
    public function isAlive()
    {
        return $this->isAlive;
    }
 
    public function markAlive()
    {
        $this->failedPings = 0;
        $this->isAlive = true;
        $this->lastPing = time();
    }
 
    public function markDead()
    {
        $this->isAlive = false;
        $this->failedPings += 1;
        $this->lastPing = time();
    }
 
    /**
     * @return int
     */
    public function getLastPing()
    {
        return $this->lastPing;
    }
 
    /**
     * @return int
     */
    public function getPingFailures()
    {
        return $this->failedPings;
    }
 
    /**
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }
 
    /**
     * @param $request
     * @param $response
     * @return \Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost|\Elasticsearch\Common\Exceptions\Curl\CouldNotResolveHostException|\Elasticsearch\Common\Exceptions\Curl\OperationTimeoutException|\Elasticsearch\Common\Exceptions\MaxRetriesException
     */
    protected function getCurlRetryException($request, $response)
    {
        $exception = null;
        $message = $response['error']->getMessage();
        $exception = new MaxRetriesException($message);
        switch ($response['curl']['errno']) {
            case 6:
                $exception = new CouldNotResolveHostException($message, null, $exception);
                break;
            case 7:
                $exception = new CouldNotConnectToHost($message, null, $exception);
                break;
            case 28:
                $exception = new OperationTimeoutException($message, null, $exception);
                break;
        }
 
        return $exception;
    }
 
    /**
     * Construct a string cURL command
     *
     * @param string $method HTTP method
     * @param string $uri    Full URI of request
     * @param string $body   Request body
     *
     * @return string
     */
    private function buildCurlCommand($method, $uri, $body)
    {
        if (strpos($uri, '?') === false) {
            $uri .= '?pretty=true';
        } else {
            str_replace('?', '?pretty=true', $uri);
        }
 
        $curlCommand = 'curl -X' . strtoupper($method);
        $curlCommand .= " '" . $uri . "'";
 
        if (isset($body) === true && $body !== '') {
            $curlCommand .= " -d '" . $body . "'";
        }
 
        return $curlCommand;
    }
 
    /**
     * @param $request
     * @param $response
     * @param $ignore
     * @throws \Elasticsearch\Common\Exceptions\AlreadyExpiredException|\Elasticsearch\Common\Exceptions\BadRequest400Exception|\Elasticsearch\Common\Exceptions\Conflict409Exception|\Elasticsearch\Common\Exceptions\Forbidden403Exception|\Elasticsearch\Common\Exceptions\Missing404Exception|\Elasticsearch\Common\Exceptions\ScriptLangNotSupportedException|null
     */
    private function process4xxError($request, $response, $ignore)
    {
        $statusCode = $response['status'];
        $responseBody = $response['body'];
 
        /** @var \Exception $exception */
        $exception = $this->tryDeserialize400Error($response);
 
        if (array_search($response['status'], $ignore) !== false) {
            return;
        }
 
        if ($statusCode === 400 && strpos($responseBody, "AlreadyExpiredException") !== false) {
            $exception = new AlreadyExpiredException($responseBody, $statusCode);
        } elseif ($statusCode === 403) {
            $exception = new Forbidden403Exception($responseBody, $statusCode);
        } elseif ($statusCode === 404) {
            $exception = new Missing404Exception($responseBody, $statusCode);
        } elseif ($statusCode === 409) {
            $exception = new Conflict409Exception($responseBody, $statusCode);
        } elseif ($statusCode === 400 && strpos($responseBody, 'script_lang not supported') !== false) {
            $exception = new ScriptLangNotSupportedException($responseBody. $statusCode);
        } elseif ($statusCode === 408 ) {
            $exception = new RequestTimeout408Exception($responseBody, $statusCode);
        }
 
        $this->logRequestFail(
            $request['http_method'],
            $response['effective_url'],
            $request['body'],
            $request['headers'],
            $response['status'],
            $response['body'],
            $response['transfer_stats']['total_time'],
            $exception
        );
 
        throw $exception;
    }
 
    /**
     * @param $request
     * @param $response
     * @param $ignore
     * @throws \Elasticsearch\Common\Exceptions\NoDocumentsToGetException|\Elasticsearch\Common\Exceptions\NoShardAvailableException|\Elasticsearch\Common\Exceptions\RoutingMissingException|\Elasticsearch\Common\Exceptions\ServerErrorResponseException
     */
    private function process5xxError($request, $response, $ignore)
    {
        $statusCode = $response['status'];
        $responseBody = $response['body'];
 
        /** @var \Exception $exception */
        $exception = $this->tryDeserialize500Error($response);
 
        $exceptionText = "[$statusCode Server Exception] ".$exception->getMessage();
        $this->log->error($exceptionText);
        $this->log->error($exception->getTraceAsString());
 
        if (array_search($statusCode, $ignore) !== false) {
            return;
        }
 
        if ($statusCode === 500 && strpos($responseBody, "RoutingMissingException") !== false) {
            $exception = new RoutingMissingException($exception->getMessage(), $statusCode, $exception);
        } elseif ($statusCode === 500 && preg_match('/ActionRequestValidationException.+ no documents to get/', $responseBody) === 1) {
            $exception = new NoDocumentsToGetException($exception->getMessage(), $statusCode, $exception);
        } elseif ($statusCode === 500 && strpos($responseBody, 'NoShardAvailableActionException') !== false) {
            $exception = new NoShardAvailableException($exception->getMessage(), $statusCode, $exception);
        }
 
        $this->logRequestFail(
            $request['http_method'],
            $response['effective_url'],
            $request['body'],
            $request['headers'],
            $response['status'],
            $response['body'],
            $response['transfer_stats']['total_time'],
            $exception
        );
 
        throw $exception;
    }
 
    private function tryDeserialize400Error($response) {
        return $this->tryDeserializeError($response, 'Elasticsearch\Common\Exceptions\BadRequest400Exception');
    }
 
    private function tryDeserialize500Error($response) {
        return $this->tryDeserializeError($response, 'Elasticsearch\Common\Exceptions\ServerErrorResponseException');
    }
 
    private function tryDeserializeError($response, $errorClass) {
        $error = $this->serializer->deserialize($response['body'], $response['transfer_stats']);
        if (is_array($error) === true) {
            // 2.0 structured exceptions
            if (isset($error['error']['reason']) === true) {
 
                // Try to use root cause first (only grabs the first root cause)
                $root = $error['error']['root_cause'];
                if (isset($root) && isset($root[0])) {
                    $cause = $root[0]['reason'];
                    $type = $root[0]['type'];
                } else {
                    $cause = $error['error']['reason'];
                    $type = $error['error']['type'];
                }
 
                $original = new $errorClass($response['body'], $response['status']);
 
                return new $errorClass("$type: $cause", $response['status'], $original);
 
            } elseif (isset($error['error']) === true) {
                // <2.0 semi-structured exceptions
                $original = new $errorClass($response['body'], $response['status']);
 
                return new $errorClass($error['error'], $response['status'], $original);
            }
 
            // <2.0 "i just blew up" nonstructured exception
            // $error is an array but we don't know the format, reuse the response body instead
            return new $errorClass($response['body'], $response['status']);
 
        }
 
        // <2.0 "i just blew up" nonstructured exception
        return new $errorClass($error, $response['status']);
    }
}
#1Elasticsearch\Connections\Connection->Elasticsearch\Connections\{closure}(Array([transfer_stats] => Array(28), [curl] => Array([error] => (empty string), [errno] => 0), [effective_url] => https://search-partsit-amts77ss2mcide4iwmj4hu6f6e.eu-west-1.es.amazonaws.com:443/partsit/parts/_search, [headers] => Array([Access-Control-Allow-Origin] => Array([0] => *), [Content-Type] => Array([0] => application/json; charset=UTF-8), [Content-Length] => Array([0] => 65), [Connection] => Array([0] => keep-alive)), [version] => 1.1, [status] => 404, [reason] => Not Found, [body] => Resource id #24))
/home/ubuntu/partsit-site/vendor/react/promise/src/FulfilledPromise.php (25)
<?php
 
namespace React\Promise;
 
class FulfilledPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
{
    private $value;
 
    public function __construct($value = null)
    {
        if ($value instanceof PromiseInterface) {
            throw new \InvalidArgumentException('You cannot create React\Promise\FulfilledPromise with a promise. Use React\Promise\resolve($promiseOrValue) instead.');
        }
 
        $this->value = $value;
    }
 
    public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
    {
        if (null === $onFulfilled) {
            return $this;
        }
 
        try {
            return resolve($onFulfilled($this->value));
        } catch (\Exception $exception) {
            return new RejectedPromise($exception);
        }
    }
 
    public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
    {
        if (null === $onFulfilled) {
            return;
        }
 
        $result = $onFulfilled($this->value);
 
        if ($result instanceof ExtendedPromiseInterface) {
            $result->done();
        }
    }
 
    public function otherwise(callable $onRejected)
    {
        return $this;
    }
 
    public function always(callable $onFulfilledOrRejected)
    {
        return $this->then(function ($value) use ($onFulfilledOrRejected) {
            return resolve($onFulfilledOrRejected())->then(function () use ($value) {
                return $value;
            });
        });
    }
 
    public function progress(callable $onProgress)
    {
        return $this;
    }
 
    public function cancel()
    {
    }
}
#2React\Promise\FulfilledPromise->then(Object(Closure), null, null)
/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php (55)
<?php
namespace GuzzleHttp\Ring\Future;
 
use React\Promise\FulfilledPromise;
use React\Promise\RejectedPromise;
 
/**
 * Represents a future value that has been resolved or rejected.
 */
class CompletedFutureValue implements FutureInterface
{
    protected $result;
    protected $error;
 
    private $cachedPromise;
 
    /**
     * @param mixed      $result Resolved result
     * @param \Exception $e      Error. Pass a GuzzleHttp\Ring\Exception\CancelledFutureAccessException
     *                           to mark the future as cancelled.
     */
    public function __construct($result, \Exception $e = null)
    {
        $this->result = $result;
        $this->error = $e;
    }
 
    public function wait()
    {
        if ($this->error) {
            throw $this->error;
        }
 
        return $this->result;
    }
 
    public function cancel() {}
 
    public function promise()
    {
        if (!$this->cachedPromise) {
            $this->cachedPromise = $this->error
                ? new RejectedPromise($this->error)
                : new FulfilledPromise($this->result);
        }
 
        return $this->cachedPromise;
    }
 
    public function then(
        callable $onFulfilled = null,
        callable $onRejected = null,
        callable $onProgress = null
    ) {
        return $this->promise()->then($onFulfilled, $onRejected, $onProgress);
    }
}
#3GuzzleHttp\Ring\Future\CompletedFutureValue->then(Object(Closure), null, null)
/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Core.php (341)
<?php
namespace GuzzleHttp\Ring;
 
use GuzzleHttp\Stream\StreamInterface;
use GuzzleHttp\Ring\Future\FutureArrayInterface;
use GuzzleHttp\Ring\Future\FutureArray;
 
/**
 * Provides core functionality of Ring handlers and middleware.
 */
class Core
{
    /**
     * Returns a function that calls all of the provided functions, in order,
     * passing the arguments provided to the composed function to each function.
     *
     * @param callable[] $functions Array of functions to proxy to.
     *
     * @return callable
     */
    public static function callArray(array $functions)
    {
        return function () use ($functions) {
            $args = func_get_args();
            foreach ($functions as $fn) {
                call_user_func_array($fn, $args);
            }
        };
    }
 
    /**
     * Gets an array of header line values from a message for a specific header
     *
     * This method searches through the "headers" key of a message for a header
     * using a case-insensitive search.
     *
     * @param array  $message Request or response hash.
     * @param string $header  Header to retrieve
     *
     * @return array
     */
    public static function headerLines($message, $header)
    {
        $result = [];
 
        if (!empty($message['headers'])) {
            foreach ($message['headers'] as $name => $value) {
                if (!strcasecmp($name, $header)) {
                    $result = array_merge($result, $value);
                }
            }
        }
 
        return $result;
    }
 
    /**
     * Gets a header value from a message as a string or null
     *
     * This method searches through the "headers" key of a message for a header
     * using a case-insensitive search. The lines of the header are imploded
     * using commas into a single string return value.
     *
     * @param array  $message Request or response hash.
     * @param string $header  Header to retrieve
     *
     * @return string|null Returns the header string if found, or null if not.
     */
    public static function header($message, $header)
    {
        $match = self::headerLines($message, $header);
        return $match ? implode(', ', $match) : null;
    }
 
    /**
     * Returns the first header value from a message as a string or null. If
     * a header line contains multiple values separated by a comma, then this
     * function will return the first value in the list.
     *
     * @param array  $message Request or response hash.
     * @param string $header  Header to retrieve
     *
     * @return string|null Returns the value as a string if found.
     */
    public static function firstHeader($message, $header)
    {
        if (!empty($message['headers'])) {
            foreach ($message['headers'] as $name => $value) {
                if (!strcasecmp($name, $header)) {
                    // Return the match itself if it is a single value.
                    $pos = strpos($value[0], ',');
                    return $pos ? substr($value[0], 0, $pos) : $value[0];
                }
            }
        }
 
        return null;
    }
 
    /**
     * Returns true if a message has the provided case-insensitive header.
     *
     * @param array  $message Request or response hash.
     * @param string $header  Header to check
     *
     * @return bool
     */
    public static function hasHeader($message, $header)
    {
        if (!empty($message['headers'])) {
            foreach ($message['headers'] as $name => $value) {
                if (!strcasecmp($name, $header)) {
                    return true;
                }
            }
        }
 
        return false;
    }
 
    /**
     * Parses an array of header lines into an associative array of headers.
     *
     * @param array $lines Header lines array of strings in the following
     *                     format: "Name: Value"
     * @return array
     */
    public static function headersFromLines($lines)
    {
        $headers = [];
 
        foreach ($lines as $line) {
            $parts = explode(':', $line, 2);
            $headers[trim($parts[0])][] = isset($parts[1])
                ? trim($parts[1])
                : null;
        }
 
        return $headers;
    }
 
    /**
     * Removes a header from a message using a case-insensitive comparison.
     *
     * @param array  $message Message that contains 'headers'
     * @param string $header  Header to remove
     *
     * @return array
     */
    public static function removeHeader(array $message, $header)
    {
        if (isset($message['headers'])) {
            foreach (array_keys($message['headers']) as $key) {
                if (!strcasecmp($header, $key)) {
                    unset($message['headers'][$key]);
                }
            }
        }
 
        return $message;
    }
 
    /**
     * Replaces any existing case insensitive headers with the given value.
     *
     * @param array  $message Message that contains 'headers'
     * @param string $header  Header to set.
     * @param array  $value   Value to set.
     *
     * @return array
     */
    public static function setHeader(array $message, $header, array $value)
    {
        $message = self::removeHeader($message, $header);
        $message['headers'][$header] = $value;
 
        return $message;
    }
 
    /**
     * Creates a URL string from a request.
     *
     * If the "url" key is present on the request, it is returned, otherwise
     * the url is built up based on the scheme, host, uri, and query_string
     * request values.
     *
     * @param array $request Request to get the URL from
     *
     * @return string Returns the request URL as a string.
     * @throws \InvalidArgumentException if no Host header is present.
     */
    public static function url(array $request)
    {
        if (isset($request['url'])) {
            return $request['url'];
        }
 
        $uri = (isset($request['scheme'])
                ? $request['scheme'] : 'http') . '://';
 
        if ($host = self::header($request, 'host')) {
            $uri .= $host;
        } else {
            throw new \InvalidArgumentException('No Host header was provided');
        }
 
        if (isset($request['uri'])) {
            $uri .= $request['uri'];
        }
 
        if (isset($request['query_string'])) {
            $uri .= '?' . $request['query_string'];
        }
 
        return $uri;
    }
 
    /**
     * Reads the body of a message into a string.
     *
     * @param array|FutureArrayInterface $message Array containing a "body" key
     *
     * @return null|string Returns the body as a string or null if not set.
     * @throws \InvalidArgumentException if a request body is invalid.
     */
    public static function body($message)
    {
        if (!isset($message['body'])) {
            return null;
        }
 
        if ($message['body'] instanceof StreamInterface) {
            return (string) $message['body'];
        }
 
        switch (gettype($message['body'])) {
            case 'string':
                return $message['body'];
            case 'resource':
                return stream_get_contents($message['body']);
            case 'object':
                if ($message['body'] instanceof \Iterator) {
                    return implode('', iterator_to_array($message['body']));
                } elseif (method_exists($message['body'], '__toString')) {
                    return (string) $message['body'];
                }
            default:
                throw new \InvalidArgumentException('Invalid request body: '
                    . self::describeType($message['body']));
        }
    }
 
    /**
     * Rewind the body of the provided message if possible.
     *
     * @param array $message Message that contains a 'body' field.
     *
     * @return bool Returns true on success, false on failure
     */
    public static function rewindBody($message)
    {
        if ($message['body'] instanceof StreamInterface) {
            return $message['body']->seek(0);
        }
 
        if ($message['body'] instanceof \Generator) {
            return false;
        }
 
        if ($message['body'] instanceof \Iterator) {
            $message['body']->rewind();
            return true;
        }
 
        if (is_resource($message['body'])) {
            return rewind($message['body']);
        }
 
        return is_string($message['body'])
            || (is_object($message['body'])
                && method_exists($message['body'], '__toString'));
    }
 
    /**
     * Debug function used to describe the provided value type and class.
     *
     * @param mixed $input
     *
     * @return string Returns a string containing the type of the variable and
     *                if a class is provided, the class name.
     */
    public static function describeType($input)
    {
        switch (gettype($input)) {
            case 'object':
                return 'object(' . get_class($input) . ')';
            case 'array':
                return 'array(' . count($input) . ')';
            default:
                ob_start();
                var_dump($input);
                // normalize float vs double
                return str_replace('double(', 'float(', rtrim(ob_get_clean()));
        }
    }
 
    /**
     * Sleep for the specified amount of time specified in the request's
     * ['client']['delay'] option if present.
     *
     * This function should only be used when a non-blocking sleep is not
     * possible.
     *
     * @param array $request Request to sleep
     */
    public static function doSleep(array $request)
    {
        if (isset($request['client']['delay'])) {
            usleep($request['client']['delay'] * 1000);
        }
    }
 
    /**
     * Returns a proxied future that modifies the dereferenced value of another
     * future using a promise.
     *
     * @param FutureArrayInterface $future      Future to wrap with a new future
     * @param callable    $onFulfilled Invoked when the future fulfilled
     * @param callable    $onRejected  Invoked when the future rejected
     * @param callable    $onProgress  Invoked when the future progresses
     *
     * @return FutureArray
     */
    public static function proxy(
        FutureArrayInterface $future,
        callable $onFulfilled = null,
        callable $onRejected = null,
        callable $onProgress = null
    ) {
        return new FutureArray(
            $future->then($onFulfilled, $onRejected, $onProgress),
            [$future, 'wait'],
            [$future, 'cancel']
        );
    }
 
    /**
     * Returns a debug stream based on the provided variable.
     *
     * @param mixed $value Optional value
     *
     * @return resource
     */
    public static function getDebugResource($value = null)
    {
        if (is_resource($value)) {
            return $value;
        } elseif (defined('STDOUT')) {
            return STDOUT;
        } else {
            return fopen('php://output', 'w');
        }
    }
}
#4GuzzleHttp\Ring\Core::proxy(Object(GuzzleHttp\Ring\Future\CompletedFutureArray), Object(Closure))
/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php (283)
<?php
 
namespace Elasticsearch\Connections;
 
use Elasticsearch\Common\Exceptions\AlreadyExpiredException;
use Elasticsearch\Common\Exceptions\BadRequest400Exception;
use Elasticsearch\Common\Exceptions\Conflict409Exception;
use Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost;
use Elasticsearch\Common\Exceptions\Curl\CouldNotResolveHostException;
use Elasticsearch\Common\Exceptions\Curl\OperationTimeoutException;
use Elasticsearch\Common\Exceptions\Forbidden403Exception;
use Elasticsearch\Common\Exceptions\MaxRetriesException;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use Elasticsearch\Common\Exceptions\NoDocumentsToGetException;
use Elasticsearch\Common\Exceptions\NoShardAvailableException;
use Elasticsearch\Common\Exceptions\RequestTimeout408Exception;
use Elasticsearch\Common\Exceptions\RoutingMissingException;
use Elasticsearch\Common\Exceptions\ScriptLangNotSupportedException;
use Elasticsearch\Common\Exceptions\ServerErrorResponseException;
use Elasticsearch\Common\Exceptions\TransportException;
use Elasticsearch\Serializers\SerializerInterface;
use Elasticsearch\Transport;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Exception\ConnectException;
use GuzzleHttp\Ring\Exception\RingException;
use Psr\Log\LoggerInterface;
 
/**
 * Class AbstractConnection
 *
 * @category Elasticsearch
 * @package  Elasticsearch\Connections
 * @author   Zachary Tong <zachary.tong@elasticsearch.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
 * @link     http://elasticsearch.org
 */
class Connection implements ConnectionInterface
{
    /** @var  callable */
    protected $handler;
 
    /** @var SerializerInterface */
    protected $serializer;
 
    /**
     * @var string
     */
    protected $transportSchema = 'http';    // TODO depreciate this default
 
    /**
     * @var string
     */
    protected $host;
 
    /**
     * @var string || null
     */
    protected $path;
 
    /**
     * @var LoggerInterface
     */
    protected $log;
 
    /**
     * @var LoggerInterface
     */
    protected $trace;
 
    /**
     * @var array
     */
    protected $connectionParams;
 
    /** @var bool  */
    protected $isAlive = false;
 
    /** @var float  */
    private $pingTimeout = 1;    //TODO expose this
 
    /** @var int  */
    private $lastPing = 0;
 
    /** @var int  */
    private $failedPings = 0;
 
    private $lastRequest = array();
 
    /**
     * Constructor
     *
     * @param $handler
     * @param array $hostDetails
     * @param array $connectionParams Array of connection-specific parameters
     * @param \Elasticsearch\Serializers\SerializerInterface $serializer
     * @param \Psr\Log\LoggerInterface $log              Logger object
     * @param \Psr\Log\LoggerInterface $trace
     */
    public function __construct($handler, $hostDetails, $connectionParams,
                                SerializerInterface $serializer, LoggerInterface $log, LoggerInterface $trace)
    {
        if (isset($hostDetails['port']) !== true) {
            $hostDetails['port'] = 9200;
        }
 
        if (isset($hostDetails['scheme'])) {
            $this->transportSchema = $hostDetails['scheme'];
        }
 
        if (isset($hostDetails['user']) && isset($hostDetails['pass'])) {
            $connectionParams['client']['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
            $connectionParams['client']['curl'][CURLOPT_USERPWD] = $hostDetails['user'].':'.$hostDetails['pass'];
        }
 
        $host = $hostDetails['host'].':'.$hostDetails['port'];
        $path = null;
        if (isset($hostDetails['path']) === true) {
            $path = $hostDetails['path'];
        }
        $this->host             = $host;
        $this->path             = $path;
        $this->log              = $log;
        $this->trace            = $trace;
        $this->connectionParams = $connectionParams;
        $this->serializer       = $serializer;
 
        $this->handler = $this->wrapHandler($handler, $log, $trace);
    }
 
    /**
     * @param $method
     * @param $uri
     * @param null $params
     * @param null $body
     * @param array $options
     * @param \Elasticsearch\Transport $transport
     * @return mixed
     */
    public function performRequest($method, $uri, $params = null, $body = null, $options = [], Transport $transport = null)
    {
        if (isset($body) === true) {
            $body = $this->serializer->serialize($body);
        }
 
        $request = [
            'http_method' => $method,
            'scheme'      => $this->transportSchema,
            'uri'         => $this->getURI($uri, $params),
            'body'        => $body,
            'headers'     => [
                'host'  => [$this->host]
            ]
 
        ];
        $request = array_merge_recursive($request, $this->connectionParams, $options);
 
 
        $handler = $this->handler;
        $future = $handler($request, $this, $transport, $options);
 
        return $future;
    }
 
    /** @return string */
    public function getTransportSchema()
    {
        return $this->transportSchema;
    }
 
    /** @return array */
    public function getLastRequestInfo()
    {
        return $this->lastRequest;
    }
 
    private function wrapHandler(callable $handler, LoggerInterface $logger, LoggerInterface $tracer)
    {
        return function (array $request, Connection $connection, Transport $transport = null, $options) use ($handler, $logger, $tracer) {
 
            $this->lastRequest = [];
            $this->lastRequest['request'] = $request;
 
            // Send the request using the wrapped handler.
            $response =  Core::proxy($handler($request), function ($response) use ($connection, $transport, $logger, $tracer, $request, $options) {
 
                $this->lastRequest['response'] = $response;
 
                if (isset($response['error']) === true) {
                    if ($response['error'] instanceof ConnectException || $response['error'] instanceof RingException) {
 
                        $this->log->warning("Curl exception encountered.");
 
                        $exception = $this->getCurlRetryException($request, $response);
 
                        $this->logRequestFail(
                            $request['http_method'],
                            $response['effective_url'],
                            $request['body'],
                            $request['headers'],
                            $response['status'],
                            $response['body'],
                            $response['transfer_stats']['total_time'],
                            $exception
                        );
 
                        $node = $connection->getHost();
                        $this->log->warning("Marking node $node dead.");
                        $connection->markDead();
 
                        // If the transport has not been set, we are inside a Ping or Sniff,
                        // so we don't want to retrigger retries anyway.
                        //
                        // TODO this could be handled better, but we are limited because connectionpools do not
                        // have access to Transport.  Architecturally, all of this needs to be refactored
                        if (isset($transport) === true) {
                            $transport->connectionPool->scheduleCheck();
 
                            $neverRetry = isset($request['client']['never_retry']) ? $request['client']['never_retry'] : false;
                            $shouldRetry = $transport->shouldRetry($request);
                            $shouldRetryText = ($shouldRetry) ? 'true' : 'false';
 
                            $this->log->warning("Retries left? $shouldRetryText");
                            if ($shouldRetry && !$neverRetry) {
                                return $transport->performRequest(
                                    $request['http_method'],
                                    $request['uri'],
                                    [],
                                    $request['body'],
                                    $options
                                );
                            }
                        }
 
                        $this->log->warning("Out of retries, throwing exception from $node");
                        // Only throw if we run out of retries
                        throw $exception;
                    } else {
                        // Something went seriously wrong, bail
                        $exception = new TransportException($response['error']->getMessage());
                        $this->logRequestFail(
                            $request['http_method'],
                            $response['effective_url'],
                            $request['body'],
                            $request['headers'],
                            $response['status'],
                            $response['body'],
                            $response['transfer_stats']['total_time'],
                            $exception
                        );
                        throw $exception;
                    }
                } else {
                    $connection->markAlive();
 
                    if (isset($response['body']) === true) {
                        $response['body'] = stream_get_contents($response['body']);
                        $this->lastRequest['response']['body'] = $response['body'];
                    }
 
                    if ($response['status'] >= 400 && $response['status'] < 500) {
                        $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : [];
                        $this->process4xxError($request, $response, $ignore);
                    } elseif ($response['status'] >= 500) {
                        $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : [];
                        $this->process5xxError($request, $response, $ignore);
                    }
 
                    // No error, deserialize
                    $response['body'] = $this->serializer->deserialize($response['body'], $response['transfer_stats']);
                }
                $this->logRequestSuccess(
                    $request['http_method'],
                    $response['effective_url'],
                    $request['body'],
                    $request['headers'],
                    $response['status'],
                    $response['body'],
                    $response['transfer_stats']['total_time']
                );
 
                return isset($request['client']['verbose']) && $request['client']['verbose'] === true ? $response : $response['body'];
 
            });
 
            return $response;
        };
    }
 
    /**
     * @param string $uri
     * @param array $params
     *
     * @return string
     */
    private function getURI($uri, $params)
    {
        if (isset($params) === true && !empty($params)) {
            $uri .= '?' . http_build_query($params);
        }
 
        if ($this->path !== null) {
            $uri = $this->path . $uri;
        }
 
        return $uri;
    }
 
    /**
     * Log a successful request
     *
     * @param string $method
     * @param string $fullURI
     * @param string $body
     * @param array  $headers
     * @param string $statusCode
     * @param string $response
     * @param string $duration
     *
     * @return void
     */
    public function logRequestSuccess($method, $fullURI, $body, $headers, $statusCode, $response, $duration)
    {
        $this->log->debug('Request Body', array($body));
        $this->log->info(
            'Request Success:',
            array(
                'method'    => $method,
                'uri'       => $fullURI,
                'headers'   => $headers,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
        $this->log->debug('Response', array($response));
 
        // Build the curl command for Trace.
        $curlCommand = $this->buildCurlCommand($method, $fullURI, $body);
        $this->trace->info($curlCommand);
        $this->trace->debug(
            'Response:',
            array(
                'response'  => $response,
                'method'    => $method,
                'uri'       => $fullURI,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
    }
 
    /**
     * Log a a failed request
     *
     * @param string $method
     * @param string $fullURI
     * @param string $body
     * @param array $headers
     * @param null|string $statusCode
     * @param null|string $response
     * @param string $duration
     * @param \Exception|null $exception
     *
     * @return void
     */
    public function logRequestFail($method, $fullURI, $body, $headers, $statusCode, $response, $duration, \Exception $exception)
    {
        $this->log->debug('Request Body', array($body));
        $this->log->warning(
            'Request Failure:',
            array(
                'method'    => $method,
                'uri'       => $fullURI,
                'headers'   => $headers,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
                'error'     => $exception->getMessage(),
            )
        );
        $this->log->warning('Response', array($response));
 
        // Build the curl command for Trace.
        $curlCommand = $this->buildCurlCommand($method, $fullURI, $body);
        $this->trace->info($curlCommand);
        $this->trace->debug(
            'Response:',
            array(
                'response'  => $response,
                'method'    => $method,
                'uri'       => $fullURI,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
    }
 
    /**
     * @return bool
     */
    public function ping()
    {
        $options = [
            'client' => [
                'timeout' => $this->pingTimeout,
                'never_retry' => true,
                'verbose' => true
            ]
        ];
        try {
            $response = $this->performRequest('HEAD', '/', null, null, $options);
            $response = $response->wait();
        } catch (TransportException $exception) {
            $this->markDead();
 
            return false;
        }
 
        if ($response['status'] === 200) {
            $this->markAlive();
 
            return true;
        } else {
            $this->markDead();
 
            return false;
        }
    }
 
    /**
     * @return array
     */
    public function sniff()
    {
        $options = [
            'client' => [
                'timeout' => $this->pingTimeout,
                'never_retry' => true
            ]
        ];
 
        return $this->performRequest('GET', '/_nodes/_all/clear', null, null, $options);
    }
 
    /**
     * @return bool
     */
    public function isAlive()
    {
        return $this->isAlive;
    }
 
    public function markAlive()
    {
        $this->failedPings = 0;
        $this->isAlive = true;
        $this->lastPing = time();
    }
 
    public function markDead()
    {
        $this->isAlive = false;
        $this->failedPings += 1;
        $this->lastPing = time();
    }
 
    /**
     * @return int
     */
    public function getLastPing()
    {
        return $this->lastPing;
    }
 
    /**
     * @return int
     */
    public function getPingFailures()
    {
        return $this->failedPings;
    }
 
    /**
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }
 
    /**
     * @param $request
     * @param $response
     * @return \Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost|\Elasticsearch\Common\Exceptions\Curl\CouldNotResolveHostException|\Elasticsearch\Common\Exceptions\Curl\OperationTimeoutException|\Elasticsearch\Common\Exceptions\MaxRetriesException
     */
    protected function getCurlRetryException($request, $response)
    {
        $exception = null;
        $message = $response['error']->getMessage();
        $exception = new MaxRetriesException($message);
        switch ($response['curl']['errno']) {
            case 6:
                $exception = new CouldNotResolveHostException($message, null, $exception);
                break;
            case 7:
                $exception = new CouldNotConnectToHost($message, null, $exception);
                break;
            case 28:
                $exception = new OperationTimeoutException($message, null, $exception);
                break;
        }
 
        return $exception;
    }
 
    /**
     * Construct a string cURL command
     *
     * @param string $method HTTP method
     * @param string $uri    Full URI of request
     * @param string $body   Request body
     *
     * @return string
     */
    private function buildCurlCommand($method, $uri, $body)
    {
        if (strpos($uri, '?') === false) {
            $uri .= '?pretty=true';
        } else {
            str_replace('?', '?pretty=true', $uri);
        }
 
        $curlCommand = 'curl -X' . strtoupper($method);
        $curlCommand .= " '" . $uri . "'";
 
        if (isset($body) === true && $body !== '') {
            $curlCommand .= " -d '" . $body . "'";
        }
 
        return $curlCommand;
    }
 
    /**
     * @param $request
     * @param $response
     * @param $ignore
     * @throws \Elasticsearch\Common\Exceptions\AlreadyExpiredException|\Elasticsearch\Common\Exceptions\BadRequest400Exception|\Elasticsearch\Common\Exceptions\Conflict409Exception|\Elasticsearch\Common\Exceptions\Forbidden403Exception|\Elasticsearch\Common\Exceptions\Missing404Exception|\Elasticsearch\Common\Exceptions\ScriptLangNotSupportedException|null
     */
    private function process4xxError($request, $response, $ignore)
    {
        $statusCode = $response['status'];
        $responseBody = $response['body'];
 
        /** @var \Exception $exception */
        $exception = $this->tryDeserialize400Error($response);
 
        if (array_search($response['status'], $ignore) !== false) {
            return;
        }
 
        if ($statusCode === 400 && strpos($responseBody, "AlreadyExpiredException") !== false) {
            $exception = new AlreadyExpiredException($responseBody, $statusCode);
        } elseif ($statusCode === 403) {
            $exception = new Forbidden403Exception($responseBody, $statusCode);
        } elseif ($statusCode === 404) {
            $exception = new Missing404Exception($responseBody, $statusCode);
        } elseif ($statusCode === 409) {
            $exception = new Conflict409Exception($responseBody, $statusCode);
        } elseif ($statusCode === 400 && strpos($responseBody, 'script_lang not supported') !== false) {
            $exception = new ScriptLangNotSupportedException($responseBody. $statusCode);
        } elseif ($statusCode === 408 ) {
            $exception = new RequestTimeout408Exception($responseBody, $statusCode);
        }
 
        $this->logRequestFail(
            $request['http_method'],
            $response['effective_url'],
            $request['body'],
            $request['headers'],
            $response['status'],
            $response['body'],
            $response['transfer_stats']['total_time'],
            $exception
        );
 
        throw $exception;
    }
 
    /**
     * @param $request
     * @param $response
     * @param $ignore
     * @throws \Elasticsearch\Common\Exceptions\NoDocumentsToGetException|\Elasticsearch\Common\Exceptions\NoShardAvailableException|\Elasticsearch\Common\Exceptions\RoutingMissingException|\Elasticsearch\Common\Exceptions\ServerErrorResponseException
     */
    private function process5xxError($request, $response, $ignore)
    {
        $statusCode = $response['status'];
        $responseBody = $response['body'];
 
        /** @var \Exception $exception */
        $exception = $this->tryDeserialize500Error($response);
 
        $exceptionText = "[$statusCode Server Exception] ".$exception->getMessage();
        $this->log->error($exceptionText);
        $this->log->error($exception->getTraceAsString());
 
        if (array_search($statusCode, $ignore) !== false) {
            return;
        }
 
        if ($statusCode === 500 && strpos($responseBody, "RoutingMissingException") !== false) {
            $exception = new RoutingMissingException($exception->getMessage(), $statusCode, $exception);
        } elseif ($statusCode === 500 && preg_match('/ActionRequestValidationException.+ no documents to get/', $responseBody) === 1) {
            $exception = new NoDocumentsToGetException($exception->getMessage(), $statusCode, $exception);
        } elseif ($statusCode === 500 && strpos($responseBody, 'NoShardAvailableActionException') !== false) {
            $exception = new NoShardAvailableException($exception->getMessage(), $statusCode, $exception);
        }
 
        $this->logRequestFail(
            $request['http_method'],
            $response['effective_url'],
            $request['body'],
            $request['headers'],
            $response['status'],
            $response['body'],
            $response['transfer_stats']['total_time'],
            $exception
        );
 
        throw $exception;
    }
 
    private function tryDeserialize400Error($response) {
        return $this->tryDeserializeError($response, 'Elasticsearch\Common\Exceptions\BadRequest400Exception');
    }
 
    private function tryDeserialize500Error($response) {
        return $this->tryDeserializeError($response, 'Elasticsearch\Common\Exceptions\ServerErrorResponseException');
    }
 
    private function tryDeserializeError($response, $errorClass) {
        $error = $this->serializer->deserialize($response['body'], $response['transfer_stats']);
        if (is_array($error) === true) {
            // 2.0 structured exceptions
            if (isset($error['error']['reason']) === true) {
 
                // Try to use root cause first (only grabs the first root cause)
                $root = $error['error']['root_cause'];
                if (isset($root) && isset($root[0])) {
                    $cause = $root[0]['reason'];
                    $type = $root[0]['type'];
                } else {
                    $cause = $error['error']['reason'];
                    $type = $error['error']['type'];
                }
 
                $original = new $errorClass($response['body'], $response['status']);
 
                return new $errorClass("$type: $cause", $response['status'], $original);
 
            } elseif (isset($error['error']) === true) {
                // <2.0 semi-structured exceptions
                $original = new $errorClass($response['body'], $response['status']);
 
                return new $errorClass($error['error'], $response['status'], $original);
            }
 
            // <2.0 "i just blew up" nonstructured exception
            // $error is an array but we don't know the format, reuse the response body instead
            return new $errorClass($response['body'], $response['status']);
 
        }
 
        // <2.0 "i just blew up" nonstructured exception
        return new $errorClass($error, $response['status']);
    }
}
#5Elasticsearch\Connections\Connection->Elasticsearch\Connections\{closure}(Array([http_method] => GET, [scheme] => https, [uri] => /partsit/parts/_search, [body] => {"query":{"query_string":{"query":"*","default_operator":"and"}},"filter":{"and":[{"bool":{"must":{"term":{"sectionID":4}}}},{"bool":{"must":{"term":{"categoryID":57}}}},{"bool":{"must":{"term":{"subCategoryID":251}}}}]}}, [headers] => Array([host] => Array([0] => search-partsit-amts77ss2mcide4iwmj4hu6f6e.eu-west-1.es.amazonaws.com:443))), Object(Elasticsearch\Connections\Connection), Object(Elasticsearch\Transport), Array())
/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php (159)
<?php
 
namespace Elasticsearch\Connections;
 
use Elasticsearch\Common\Exceptions\AlreadyExpiredException;
use Elasticsearch\Common\Exceptions\BadRequest400Exception;
use Elasticsearch\Common\Exceptions\Conflict409Exception;
use Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost;
use Elasticsearch\Common\Exceptions\Curl\CouldNotResolveHostException;
use Elasticsearch\Common\Exceptions\Curl\OperationTimeoutException;
use Elasticsearch\Common\Exceptions\Forbidden403Exception;
use Elasticsearch\Common\Exceptions\MaxRetriesException;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use Elasticsearch\Common\Exceptions\NoDocumentsToGetException;
use Elasticsearch\Common\Exceptions\NoShardAvailableException;
use Elasticsearch\Common\Exceptions\RequestTimeout408Exception;
use Elasticsearch\Common\Exceptions\RoutingMissingException;
use Elasticsearch\Common\Exceptions\ScriptLangNotSupportedException;
use Elasticsearch\Common\Exceptions\ServerErrorResponseException;
use Elasticsearch\Common\Exceptions\TransportException;
use Elasticsearch\Serializers\SerializerInterface;
use Elasticsearch\Transport;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Exception\ConnectException;
use GuzzleHttp\Ring\Exception\RingException;
use Psr\Log\LoggerInterface;
 
/**
 * Class AbstractConnection
 *
 * @category Elasticsearch
 * @package  Elasticsearch\Connections
 * @author   Zachary Tong <zachary.tong@elasticsearch.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
 * @link     http://elasticsearch.org
 */
class Connection implements ConnectionInterface
{
    /** @var  callable */
    protected $handler;
 
    /** @var SerializerInterface */
    protected $serializer;
 
    /**
     * @var string
     */
    protected $transportSchema = 'http';    // TODO depreciate this default
 
    /**
     * @var string
     */
    protected $host;
 
    /**
     * @var string || null
     */
    protected $path;
 
    /**
     * @var LoggerInterface
     */
    protected $log;
 
    /**
     * @var LoggerInterface
     */
    protected $trace;
 
    /**
     * @var array
     */
    protected $connectionParams;
 
    /** @var bool  */
    protected $isAlive = false;
 
    /** @var float  */
    private $pingTimeout = 1;    //TODO expose this
 
    /** @var int  */
    private $lastPing = 0;
 
    /** @var int  */
    private $failedPings = 0;
 
    private $lastRequest = array();
 
    /**
     * Constructor
     *
     * @param $handler
     * @param array $hostDetails
     * @param array $connectionParams Array of connection-specific parameters
     * @param \Elasticsearch\Serializers\SerializerInterface $serializer
     * @param \Psr\Log\LoggerInterface $log              Logger object
     * @param \Psr\Log\LoggerInterface $trace
     */
    public function __construct($handler, $hostDetails, $connectionParams,
                                SerializerInterface $serializer, LoggerInterface $log, LoggerInterface $trace)
    {
        if (isset($hostDetails['port']) !== true) {
            $hostDetails['port'] = 9200;
        }
 
        if (isset($hostDetails['scheme'])) {
            $this->transportSchema = $hostDetails['scheme'];
        }
 
        if (isset($hostDetails['user']) && isset($hostDetails['pass'])) {
            $connectionParams['client']['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
            $connectionParams['client']['curl'][CURLOPT_USERPWD] = $hostDetails['user'].':'.$hostDetails['pass'];
        }
 
        $host = $hostDetails['host'].':'.$hostDetails['port'];
        $path = null;
        if (isset($hostDetails['path']) === true) {
            $path = $hostDetails['path'];
        }
        $this->host             = $host;
        $this->path             = $path;
        $this->log              = $log;
        $this->trace            = $trace;
        $this->connectionParams = $connectionParams;
        $this->serializer       = $serializer;
 
        $this->handler = $this->wrapHandler($handler, $log, $trace);
    }
 
    /**
     * @param $method
     * @param $uri
     * @param null $params
     * @param null $body
     * @param array $options
     * @param \Elasticsearch\Transport $transport
     * @return mixed
     */
    public function performRequest($method, $uri, $params = null, $body = null, $options = [], Transport $transport = null)
    {
        if (isset($body) === true) {
            $body = $this->serializer->serialize($body);
        }
 
        $request = [
            'http_method' => $method,
            'scheme'      => $this->transportSchema,
            'uri'         => $this->getURI($uri, $params),
            'body'        => $body,
            'headers'     => [
                'host'  => [$this->host]
            ]
 
        ];
        $request = array_merge_recursive($request, $this->connectionParams, $options);
 
 
        $handler = $this->handler;
        $future = $handler($request, $this, $transport, $options);
 
        return $future;
    }
 
    /** @return string */
    public function getTransportSchema()
    {
        return $this->transportSchema;
    }
 
    /** @return array */
    public function getLastRequestInfo()
    {
        return $this->lastRequest;
    }
 
    private function wrapHandler(callable $handler, LoggerInterface $logger, LoggerInterface $tracer)
    {
        return function (array $request, Connection $connection, Transport $transport = null, $options) use ($handler, $logger, $tracer) {
 
            $this->lastRequest = [];
            $this->lastRequest['request'] = $request;
 
            // Send the request using the wrapped handler.
            $response =  Core::proxy($handler($request), function ($response) use ($connection, $transport, $logger, $tracer, $request, $options) {
 
                $this->lastRequest['response'] = $response;
 
                if (isset($response['error']) === true) {
                    if ($response['error'] instanceof ConnectException || $response['error'] instanceof RingException) {
 
                        $this->log->warning("Curl exception encountered.");
 
                        $exception = $this->getCurlRetryException($request, $response);
 
                        $this->logRequestFail(
                            $request['http_method'],
                            $response['effective_url'],
                            $request['body'],
                            $request['headers'],
                            $response['status'],
                            $response['body'],
                            $response['transfer_stats']['total_time'],
                            $exception
                        );
 
                        $node = $connection->getHost();
                        $this->log->warning("Marking node $node dead.");
                        $connection->markDead();
 
                        // If the transport has not been set, we are inside a Ping or Sniff,
                        // so we don't want to retrigger retries anyway.
                        //
                        // TODO this could be handled better, but we are limited because connectionpools do not
                        // have access to Transport.  Architecturally, all of this needs to be refactored
                        if (isset($transport) === true) {
                            $transport->connectionPool->scheduleCheck();
 
                            $neverRetry = isset($request['client']['never_retry']) ? $request['client']['never_retry'] : false;
                            $shouldRetry = $transport->shouldRetry($request);
                            $shouldRetryText = ($shouldRetry) ? 'true' : 'false';
 
                            $this->log->warning("Retries left? $shouldRetryText");
                            if ($shouldRetry && !$neverRetry) {
                                return $transport->performRequest(
                                    $request['http_method'],
                                    $request['uri'],
                                    [],
                                    $request['body'],
                                    $options
                                );
                            }
                        }
 
                        $this->log->warning("Out of retries, throwing exception from $node");
                        // Only throw if we run out of retries
                        throw $exception;
                    } else {
                        // Something went seriously wrong, bail
                        $exception = new TransportException($response['error']->getMessage());
                        $this->logRequestFail(
                            $request['http_method'],
                            $response['effective_url'],
                            $request['body'],
                            $request['headers'],
                            $response['status'],
                            $response['body'],
                            $response['transfer_stats']['total_time'],
                            $exception
                        );
                        throw $exception;
                    }
                } else {
                    $connection->markAlive();
 
                    if (isset($response['body']) === true) {
                        $response['body'] = stream_get_contents($response['body']);
                        $this->lastRequest['response']['body'] = $response['body'];
                    }
 
                    if ($response['status'] >= 400 && $response['status'] < 500) {
                        $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : [];
                        $this->process4xxError($request, $response, $ignore);
                    } elseif ($response['status'] >= 500) {
                        $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : [];
                        $this->process5xxError($request, $response, $ignore);
                    }
 
                    // No error, deserialize
                    $response['body'] = $this->serializer->deserialize($response['body'], $response['transfer_stats']);
                }
                $this->logRequestSuccess(
                    $request['http_method'],
                    $response['effective_url'],
                    $request['body'],
                    $request['headers'],
                    $response['status'],
                    $response['body'],
                    $response['transfer_stats']['total_time']
                );
 
                return isset($request['client']['verbose']) && $request['client']['verbose'] === true ? $response : $response['body'];
 
            });
 
            return $response;
        };
    }
 
    /**
     * @param string $uri
     * @param array $params
     *
     * @return string
     */
    private function getURI($uri, $params)
    {
        if (isset($params) === true && !empty($params)) {
            $uri .= '?' . http_build_query($params);
        }
 
        if ($this->path !== null) {
            $uri = $this->path . $uri;
        }
 
        return $uri;
    }
 
    /**
     * Log a successful request
     *
     * @param string $method
     * @param string $fullURI
     * @param string $body
     * @param array  $headers
     * @param string $statusCode
     * @param string $response
     * @param string $duration
     *
     * @return void
     */
    public function logRequestSuccess($method, $fullURI, $body, $headers, $statusCode, $response, $duration)
    {
        $this->log->debug('Request Body', array($body));
        $this->log->info(
            'Request Success:',
            array(
                'method'    => $method,
                'uri'       => $fullURI,
                'headers'   => $headers,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
        $this->log->debug('Response', array($response));
 
        // Build the curl command for Trace.
        $curlCommand = $this->buildCurlCommand($method, $fullURI, $body);
        $this->trace->info($curlCommand);
        $this->trace->debug(
            'Response:',
            array(
                'response'  => $response,
                'method'    => $method,
                'uri'       => $fullURI,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
    }
 
    /**
     * Log a a failed request
     *
     * @param string $method
     * @param string $fullURI
     * @param string $body
     * @param array $headers
     * @param null|string $statusCode
     * @param null|string $response
     * @param string $duration
     * @param \Exception|null $exception
     *
     * @return void
     */
    public function logRequestFail($method, $fullURI, $body, $headers, $statusCode, $response, $duration, \Exception $exception)
    {
        $this->log->debug('Request Body', array($body));
        $this->log->warning(
            'Request Failure:',
            array(
                'method'    => $method,
                'uri'       => $fullURI,
                'headers'   => $headers,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
                'error'     => $exception->getMessage(),
            )
        );
        $this->log->warning('Response', array($response));
 
        // Build the curl command for Trace.
        $curlCommand = $this->buildCurlCommand($method, $fullURI, $body);
        $this->trace->info($curlCommand);
        $this->trace->debug(
            'Response:',
            array(
                'response'  => $response,
                'method'    => $method,
                'uri'       => $fullURI,
                'HTTP code' => $statusCode,
                'duration'  => $duration,
            )
        );
    }
 
    /**
     * @return bool
     */
    public function ping()
    {
        $options = [
            'client' => [
                'timeout' => $this->pingTimeout,
                'never_retry' => true,
                'verbose' => true
            ]
        ];
        try {
            $response = $this->performRequest('HEAD', '/', null, null, $options);
            $response = $response->wait();
        } catch (TransportException $exception) {
            $this->markDead();
 
            return false;
        }
 
        if ($response['status'] === 200) {
            $this->markAlive();
 
            return true;
        } else {
            $this->markDead();
 
            return false;
        }
    }
 
    /**
     * @return array
     */
    public function sniff()
    {
        $options = [
            'client' => [
                'timeout' => $this->pingTimeout,
                'never_retry' => true
            ]
        ];
 
        return $this->performRequest('GET', '/_nodes/_all/clear', null, null, $options);
    }
 
    /**
     * @return bool
     */
    public function isAlive()
    {
        return $this->isAlive;
    }
 
    public function markAlive()
    {
        $this->failedPings = 0;
        $this->isAlive = true;
        $this->lastPing = time();
    }
 
    public function markDead()
    {
        $this->isAlive = false;
        $this->failedPings += 1;
        $this->lastPing = time();
    }
 
    /**
     * @return int
     */
    public function getLastPing()
    {
        return $this->lastPing;
    }
 
    /**
     * @return int
     */
    public function getPingFailures()
    {
        return $this->failedPings;
    }
 
    /**
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }
 
    /**
     * @param $request
     * @param $response
     * @return \Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost|\Elasticsearch\Common\Exceptions\Curl\CouldNotResolveHostException|\Elasticsearch\Common\Exceptions\Curl\OperationTimeoutException|\Elasticsearch\Common\Exceptions\MaxRetriesException
     */
    protected function getCurlRetryException($request, $response)
    {
        $exception = null;
        $message = $response['error']->getMessage();
        $exception = new MaxRetriesException($message);
        switch ($response['curl']['errno']) {
            case 6:
                $exception = new CouldNotResolveHostException($message, null, $exception);
                break;
            case 7:
                $exception = new CouldNotConnectToHost($message, null, $exception);
                break;
            case 28:
                $exception = new OperationTimeoutException($message, null, $exception);
                break;
        }
 
        return $exception;
    }
 
    /**
     * Construct a string cURL command
     *
     * @param string $method HTTP method
     * @param string $uri    Full URI of request
     * @param string $body   Request body
     *
     * @return string
     */
    private function buildCurlCommand($method, $uri, $body)
    {
        if (strpos($uri, '?') === false) {
            $uri .= '?pretty=true';
        } else {
            str_replace('?', '?pretty=true', $uri);
        }
 
        $curlCommand = 'curl -X' . strtoupper($method);
        $curlCommand .= " '" . $uri . "'";
 
        if (isset($body) === true && $body !== '') {
            $curlCommand .= " -d '" . $body . "'";
        }
 
        return $curlCommand;
    }
 
    /**
     * @param $request
     * @param $response
     * @param $ignore
     * @throws \Elasticsearch\Common\Exceptions\AlreadyExpiredException|\Elasticsearch\Common\Exceptions\BadRequest400Exception|\Elasticsearch\Common\Exceptions\Conflict409Exception|\Elasticsearch\Common\Exceptions\Forbidden403Exception|\Elasticsearch\Common\Exceptions\Missing404Exception|\Elasticsearch\Common\Exceptions\ScriptLangNotSupportedException|null
     */
    private function process4xxError($request, $response, $ignore)
    {
        $statusCode = $response['status'];
        $responseBody = $response['body'];
 
        /** @var \Exception $exception */
        $exception = $this->tryDeserialize400Error($response);
 
        if (array_search($response['status'], $ignore) !== false) {
            return;
        }
 
        if ($statusCode === 400 && strpos($responseBody, "AlreadyExpiredException") !== false) {
            $exception = new AlreadyExpiredException($responseBody, $statusCode);
        } elseif ($statusCode === 403) {
            $exception = new Forbidden403Exception($responseBody, $statusCode);
        } elseif ($statusCode === 404) {
            $exception = new Missing404Exception($responseBody, $statusCode);
        } elseif ($statusCode === 409) {
            $exception = new Conflict409Exception($responseBody, $statusCode);
        } elseif ($statusCode === 400 && strpos($responseBody, 'script_lang not supported') !== false) {
            $exception = new ScriptLangNotSupportedException($responseBody. $statusCode);
        } elseif ($statusCode === 408 ) {
            $exception = new RequestTimeout408Exception($responseBody, $statusCode);
        }
 
        $this->logRequestFail(
            $request['http_method'],
            $response['effective_url'],
            $request['body'],
            $request['headers'],
            $response['status'],
            $response['body'],
            $response['transfer_stats']['total_time'],
            $exception
        );
 
        throw $exception;
    }
 
    /**
     * @param $request
     * @param $response
     * @param $ignore
     * @throws \Elasticsearch\Common\Exceptions\NoDocumentsToGetException|\Elasticsearch\Common\Exceptions\NoShardAvailableException|\Elasticsearch\Common\Exceptions\RoutingMissingException|\Elasticsearch\Common\Exceptions\ServerErrorResponseException
     */
    private function process5xxError($request, $response, $ignore)
    {
        $statusCode = $response['status'];
        $responseBody = $response['body'];
 
        /** @var \Exception $exception */
        $exception = $this->tryDeserialize500Error($response);
 
        $exceptionText = "[$statusCode Server Exception] ".$exception->getMessage();
        $this->log->error($exceptionText);
        $this->log->error($exception->getTraceAsString());
 
        if (array_search($statusCode, $ignore) !== false) {
            return;
        }
 
        if ($statusCode === 500 && strpos($responseBody, "RoutingMissingException") !== false) {
            $exception = new RoutingMissingException($exception->getMessage(), $statusCode, $exception);
        } elseif ($statusCode === 500 && preg_match('/ActionRequestValidationException.+ no documents to get/', $responseBody) === 1) {
            $exception = new NoDocumentsToGetException($exception->getMessage(), $statusCode, $exception);
        } elseif ($statusCode === 500 && strpos($responseBody, 'NoShardAvailableActionException') !== false) {
            $exception = new NoShardAvailableException($exception->getMessage(), $statusCode, $exception);
        }
 
        $this->logRequestFail(
            $request['http_method'],
            $response['effective_url'],
            $request['body'],
            $request['headers'],
            $response['status'],
            $response['body'],
            $response['transfer_stats']['total_time'],
            $exception
        );
 
        throw $exception;
    }
 
    private function tryDeserialize400Error($response) {
        return $this->tryDeserializeError($response, 'Elasticsearch\Common\Exceptions\BadRequest400Exception');
    }
 
    private function tryDeserialize500Error($response) {
        return $this->tryDeserializeError($response, 'Elasticsearch\Common\Exceptions\ServerErrorResponseException');
    }
 
    private function tryDeserializeError($response, $errorClass) {
        $error = $this->serializer->deserialize($response['body'], $response['transfer_stats']);
        if (is_array($error) === true) {
            // 2.0 structured exceptions
            if (isset($error['error']['reason']) === true) {
 
                // Try to use root cause first (only grabs the first root cause)
                $root = $error['error']['root_cause'];
                if (isset($root) && isset($root[0])) {
                    $cause = $root[0]['reason'];
                    $type = $root[0]['type'];
                } else {
                    $cause = $error['error']['reason'];
                    $type = $error['error']['type'];
                }
 
                $original = new $errorClass($response['body'], $response['status']);
 
                return new $errorClass("$type: $cause", $response['status'], $original);
 
            } elseif (isset($error['error']) === true) {
                // <2.0 semi-structured exceptions
                $original = new $errorClass($response['body'], $response['status']);
 
                return new $errorClass($error['error'], $response['status'], $original);
            }
 
            // <2.0 "i just blew up" nonstructured exception
            // $error is an array but we don't know the format, reuse the response body instead
            return new $errorClass($response['body'], $response['status']);
 
        }
 
        // <2.0 "i just blew up" nonstructured exception
        return new $errorClass($error, $response['status']);
    }
}
#6Elasticsearch\Connections\Connection->performRequest(GET, /partsit/parts/_search, Array(), Array([query] => Array([query_string] => Array([query] => *, [default_operator] => and)), [filter] => Array([and] => Array([0] => Array(), [1] => Array(), [2] => Array()))), Array(), Object(Elasticsearch\Transport))
/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Transport.php (106)
<?php
 
namespace Elasticsearch;
 
use Elasticsearch\Common\Exceptions;
use Elasticsearch\ConnectionPool\AbstractConnectionPool;
use Elasticsearch\Connections\Connection;
use Elasticsearch\Connections\ConnectionInterface;
use Psr\Log\LoggerInterface;
 
/**
 * Class Transport
 *
 * @category Elasticsearch
 * @package  Elasticsearch
 * @author   Zachary Tong <zachary.tong@elasticsearch.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
 * @link     http://elasticsearch.org
 */
class Transport
{
    /**
     * @var AbstractConnectionPool
     */
    public $connectionPool;
 
    /**
     * @var LoggerInterface
     */
    private $log;
 
    /** @var  int */
    public $retryAttempts = 0;
 
    /** @var  Connection */
    public $lastConnection;
 
    /** @var int  */
    public $retries;
 
    /**
     * Transport class is responsible for dispatching requests to the
     * underlying cluster connections
     *
     * @param $retries
     * @param bool $sniffOnStart
     * @param ConnectionPool\AbstractConnectionPool $connectionPool
     * @param \Psr\Log\LoggerInterface $log    Monolog logger object
     */
    public function __construct($retries, $sniffOnStart = false, AbstractConnectionPool $connectionPool, LoggerInterface $log)
    {
        $this->log            = $log;
        $this->connectionPool = $connectionPool;
        $this->retries        = $retries;
 
        if ($sniffOnStart === true) {
            $this->log->notice('Sniff on Start.');
            $this->connectionPool->scheduleCheck();
        }
    }
 
    /**
     * Returns a single connection from the connection pool
     * Potentially performs a sniffing step before returning
     *
     * @return ConnectionInterface Connection
     */
 
    public function getConnection()
    {
        return $this->connectionPool->nextConnection();
    }
 
    /**
     * Perform a request to the Cluster
     *
     * @param string $method     HTTP method to use
     * @param string $uri        HTTP URI to send request to
     * @param null $params     Optional query parameters
     * @param null $body       Optional query body
     * @param array $options
     *
     * @throws Common\Exceptions\NoNodesAvailableException|\Exception
     * @return array
     */
    public function performRequest($method, $uri, $params = null, $body = null, $options = [])
    {
        try {
            $connection  = $this->getConnection();
        } catch (Exceptions\NoNodesAvailableException $exception) {
            $this->log->critical('No alive nodes found in cluster');
            throw $exception;
        }
 
        $response             = array();
        $caughtException      = null;
        $this->lastConnection = $connection;
 
        $future = $connection->performRequest(
            $method,
            $uri,
            $params,
            $body,
            $options,
            $this
        );
 
        $future->promise()->then(
            //onSuccess
            function ($response) {
                $this->retryAttempts = 0;
                // Note, this could be a 4xx or 5xx error
            },
            //onFailure
            function ($response) {
                //some kind of real faiure here, like a timeout
                $this->connectionPool->scheduleCheck();
                // log stuff
            });
 
        return $future;
    }
 
    /**
     * @param $request
     *
     * @return bool
     */
    public function shouldRetry($request)
    {
        if ($this->retryAttempts < $this->retries) {
            $this->retryAttempts += 1;
 
            return true;
        }
 
        return false;
    }
 
    /**
     * Returns the last used connection so that it may be inspected.  Mainly
     * for debugging/testing purposes.
     *
     * @return Connection
     */
    public function getLastConnection()
    {
        return $this->lastConnection;
    }
}
#7Elasticsearch\Transport->performRequest(GET, /partsit/parts/_search, Array(), Array([query] => Array([query_string] => Array([query] => *, [default_operator] => and)), [filter] => Array([and] => Array([0] => Array(), [1] => Array(), [2] => Array()))), Array())
/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/AbstractEndpoint.php (80)
<?php
 
namespace Elasticsearch\Endpoints;
 
use Elasticsearch\Common\Exceptions\UnexpectedValueException;
use Elasticsearch\Transport;
use Exception;
use GuzzleHttp\Ring\Future\FutureArrayInterface;
 
/**
 * Class AbstractEndpoint
 *
 * @category Elasticsearch
 * @package  Elasticsearch\Endpoints
 * @author   Zachary Tong <zachary.tong@elasticsearch.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
 * @link     http://elasticsearch.org
 */
abstract class AbstractEndpoint
{
    /** @var array  */
    protected $params = array();
 
    /** @var  string */
    protected $index = null;
 
    /** @var  string */
    protected $type = null;
 
    /** @var  string|int */
    protected $id = null;
 
    /** @var  string */
    protected $method = null;
 
    /** @var  array */
    protected $body = null;
 
    /** @var \Elasticsearch\Transport  */
    private $transport = null;
 
    /** @var array  */
    private $options = [];
 
    /**
     * @return string[]
     */
    abstract protected function getParamWhitelist();
 
    /**
     * @return string
     */
    abstract protected function getURI();
 
    /**
     * @return string
     */
    abstract protected function getMethod();
 
    /**
     * @param Transport $transport
     */
    public function __construct($transport)
    {
        $this->transport = $transport;
    }
 
    /**
     * @throws \Exception
     * @return array
     */
    public function performRequest()
    {
        $promise =  $this->transport->performRequest(
            $this->getMethod(),
            $this->getURI(),
            $this->params,
            $this->getBody(),
            $this->options
        );
 
        return $promise;
    }
 
    /**
     * Set the parameters for this endpoint
     *
     * @param string[] $params Array of parameters
     * @return $this
     */
    public function setParams($params)
    {
        if (is_object($params) === true) {
            $params = (array) $params;
        }
 
        $this->checkUserParams($params);
        $params = $this->convertCustom($params);
        $this->extractOptions($params);
        $this->params = $this->convertArraysToStrings($params);
 
        return $this;
    }
 
    /**
     * @param string $index
     *
     * @return $this
     */
    public function setIndex($index)
    {
        if ($index === null) {
            return $this;
        }
 
        if (is_array($index) === true) {
            $index = array_map('trim', $index);
            $index = implode(",", $index);
        }
 
        $this->index = urlencode($index);
 
        return $this;
    }
 
    /**
     * @param string $type
     *
     * @return $this
     */
    public function setType($type)
    {
        if ($type === null) {
            return $this;
        }
 
        if (is_array($type) === true) {
            $type = array_map('trim', $type);
            $type = implode(",", $type);
        }
 
        $this->type = urlencode($type);
 
        return $this;
    }
 
    /**
     * @param int|string $docID
     *
     * @return $this
     */
    public function setID($docID)
    {
        if ($docID === null) {
            return $this;
        }
 
        $this->id = urlencode($docID);
 
        return $this;
    }
 
    /**
     * @param $result
     * @return callable|array
     */
    public function resultOrFuture($result)
    {
        $response = null;
        $async = isset($this->options['client']['future']) ? $this->options['client']['future'] : null;
        if (is_null($async) || $async === false) {
            do {
                $result = $result->wait();
            } while ($result instanceof FutureArrayInterface);
 
            return $result;
        } elseif ($async === true || $async === 'lazy') {
            return $result;
        }
    }
 
    /**
     * @return array
     */
    protected function getBody()
    {
        return $this->body;
    }
 
    /**
     * @param string $endpoint
     *
     * @return string
     */
    protected function getOptionalURI($endpoint)
    {
        $uri = array();
        $uri[] = $this->getOptionalIndex();
        $uri[] = $this->getOptionalType();
        $uri[] = $endpoint;
        $uri =  array_filter($uri);
 
        return '/' . implode('/', $uri);
    }
 
    /**
     * @return string
     */
    private function getOptionalIndex()
    {
        if (isset($this->index) === true) {
            return $this->index;
        } else {
            return '_all';
        }
    }
 
    /**
     * @return string
     */
    private function getOptionalType()
    {
        if (isset($this->type) === true) {
            return $this->type;
        } else {
            return '';
        }
    }
 
    /**
     * @param array $params
     *
     * @throws \Elasticsearch\Common\Exceptions\UnexpectedValueException
     */
    private function checkUserParams($params)
    {
        if (isset($params) !== true) {
            return; //no params, just return.
        }
 
        $whitelist = array_merge($this->getParamWhitelist(), array('client', 'custom', 'filter_path'));
 
        foreach ($params as $key => $value) {
            if (array_search($key, $whitelist) === false) {
                throw new UnexpectedValueException(sprintf(
                    '"%s" is not a valid parameter. Allowed parameters are: "%s"',
                    $key,
                    implode('", "', $whitelist)
                ));
            }
        }
    }
 
    /**
     * @param $params       Note: this is passed by-reference!
     */
    private function extractOptions(&$params)
    {
        // Extract out client options, then start transforming
        if (isset($params['client']) === true) {
            $this->options['client'] = $params['client'];
            unset($params['client']);
        }
 
        $ignore = isset($this->options['client']['ignore']) ? $this->options['client']['ignore'] : null;
        if (isset($ignore) === true) {
            if (is_string($ignore)) {
                $this->options['client']['ignore'] = explode(",", $ignore);
            } elseif (is_array($ignore)) {
                $this->options['client']['ignore'] = $ignore;
            } else {
                $this->options['client']['ignore'] = [$ignore];
            }
        }
    }
 
    private function convertCustom($params)
    {
        if (isset($params['custom']) === true) {
            foreach ($params['custom'] as $k => $v) {
                $params[$k] = $v;
            }
            unset($params['custom']);
        }
 
        return $params;
    }
 
    private function convertArraysToStrings($params)
    {
        foreach ($params as $key => &$value) {
            if (!($key === 'client' || $key == 'custom') && is_array($value) === true) {
                if ($this->isNestedArray($value) !== true) {
                    $value = implode(",", $value);
                }
            }
        }
 
        return $params;
    }
 
    private function isNestedArray($a)
    {
        foreach ($a as $v) {
            if (is_array($v)) {
                return true;
            }
        }
 
        return false;
    }
}
#8Elasticsearch\Endpoints\AbstractEndpoint->performRequest()
/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Client.php (930)
<?php
 
namespace Elasticsearch;
 
use Elasticsearch\Common\Exceptions\InvalidArgumentException;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use Elasticsearch\Common\Exceptions\TransportException;
use Elasticsearch\Namespaces\CatNamespace;
use Elasticsearch\Namespaces\ClusterNamespace;
use Elasticsearch\Namespaces\IndicesNamespace;
use Elasticsearch\Namespaces\NodesNamespace;
use Elasticsearch\Namespaces\SnapshotNamespace;
use Elasticsearch\Namespaces\BooleanRequestWrapper;
 
/**
 * Class Client
 *
 * @category Elasticsearch
 * @package  Elasticsearch
 * @author   Zachary Tong <zachary.tong@elasticsearch.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
 * @link     http://elasticsearch.org
 */
class Client
{
    /**
     * @var Transport
     */
    public $transport;
 
    /**
     * @var array
     */
    protected $params;
 
    /**
     * @var IndicesNamespace
     */
    protected $indices;
 
    /**
     * @var ClusterNamespace
     */
    protected $cluster;
 
    /**
     * @var NodesNamespace
     */
    protected $nodes;
 
    /**
     * @var SnapshotNamespace
     */
    protected $snapshot;
 
    /**
     * @var CatNamespace
     */
    protected $cat;
 
    /** @var  callback */
    protected $endpoints;
 
    /**
     * Client constructor
     *
     * @param Transport $transport
     * @param callable $endpoint
     */
    public function __construct(Transport $transport, callable $endpoint)
    {
        $this->transport = $transport;
        $this->endpoints = $endpoint;
        $this->indices   = new IndicesNamespace($transport, $endpoint);
        $this->cluster   = new ClusterNamespace($transport, $endpoint);
        $this->nodes     = new NodesNamespace($transport, $endpoint);
        $this->snapshot  = new SnapshotNamespace($transport, $endpoint);
        $this->cat       = new CatNamespace($transport, $endpoint);
    }
 
    /**
     * @param $params
     * @return array
     */
    public function info($params = [])
    {
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Info $endpoint */
        $endpoint = $endpointBuilder('Info');
        $response = $endpoint->setParams($params)->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * @param $params array Associative array of parameters
     *
     * @return bool
     */
    public function ping($params = [])
    {
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Ping $endpoint */
        $endpoint = $endpointBuilder('Ping');
 
        try {
            $response = $endpoint->setParams($params)->performRequest();
            $endpoint->resultOrFuture($response);
        } catch (Missing404Exception $exception) {
            return false;
        } catch (TransportException $exception) {
            return false;
        }
 
        return true;
    }
 
    /**
     * $params['id']              = (string) The document ID (Required)
     *        ['index']           = (string) The name of the index (Required)
     *        ['type']            = (string) The type of the document (use `_all` to fetch the first document matching the ID across all types) (Required)
     *        ['ignore_missing']  = ??
     *        ['fields']          = (list) A comma-separated list of fields to return in the response
     *        ['parent']          = (string) The ID of the parent document
     *        ['preference']      = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['realtime']        = (boolean) Specify whether to perform the operation in realtime or search mode
     *        ['refresh']         = (boolean) Refresh the shard containing the document before performing the operation
     *        ['routing']         = (string) Specific routing value
     *        ['_source']         = (list) True or false to return the _source field or not, or a list of fields to return
     *        ['_source_exclude'] = (list) A list of fields to exclude from the returned _source field
     *        ['_source_include'] = (list) A list of fields to extract and return from the _source field
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function get($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Get $endpoint */
        $endpoint = $endpointBuilder('Get');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']             = (string) The document ID (Required)
     *        ['index']          = (string) The name of the index (Required)
     *        ['type']           = (string) The type of the document (use `_all` to fetch the first document matching the ID across all types) (Required)
     *        ['ignore_missing'] = ??
     *        ['parent']         = (string) The ID of the parent document
     *        ['preference']     = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['realtime']       = (boolean) Specify whether to perform the operation in realtime or search mode
     *        ['refresh']        = (boolean) Refresh the shard containing the document before performing the operation
     *        ['routing']        = (string) Specific routing value
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function getSource($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Get $endpoint */
        $endpoint = $endpointBuilder('Get');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type)
                 ->returnOnlySource();
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']           = (string) The document ID (Required)
     *        ['index']        = (string) The name of the index (Required)
     *        ['type']         = (string) The type of the document (Required)
     *        ['consistency']  = (enum) Specific write consistency setting for the operation
     *        ['parent']       = (string) ID of parent document
     *        ['refresh']      = (boolean) Refresh the index after performing the operation
     *        ['replication']  = (enum) Specific replication type
     *        ['routing']      = (string) Specific routing value
     *        ['timeout']      = (time) Explicit operation timeout
     *        ['version_type'] = (enum) Specific version type
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function delete($params)
    {
        $id = $this->extractArgument($params, 'id');
        $index = $this->extractArgument($params, 'index');
        $type = $this->extractArgument($params, 'type');
 
        $this->verifyNotNullOrEmpty("id", $id);
        $this->verifyNotNullOrEmpty("type", $type);
        $this->verifyNotNullOrEmpty("index", $index);
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Delete $endpoint */
        $endpoint = $endpointBuilder('Delete');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     *
     * $params[''] @todo finish the rest of these params
     *        ['ignore_unavailable'] = (bool) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['allow_no_indices']   = (bool) Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
     *        ['expand_wildcards']   = (enum) Whether to expand wildcard expression to concrete indices that are open, closed or both.
     *
     * @param array $params
     *
     * @return array
     */
    public function deleteByQuery($params = array())
    {
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\DeleteByQuery $endpoint */
        $endpoint = $endpointBuilder('DeleteByQuery');
        $endpoint->setIndex($index)
                ->setType($type)
                ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']              = (list) A comma-separated list of indices to restrict the results
     *        ['type']               = (list) A comma-separated list of types to restrict the results
     *        ['min_score']          = (number) Include only documents with a specific `_score` value in the result
     *        ['preference']         = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['routing']            = (string) Specific routing value
     *        ['source']             = (string) The URL-encoded query definition (instead of using the request body)
     *        ['body']               = (array) A query to restrict the results (optional)
     *        ['ignore_unavailable'] = (bool) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['allow_no_indices']   = (bool) Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
     *        ['expand_wildcards']   = (enum) Whether to expand wildcard expression to concrete indices that are open, closed or both.
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function count($params = array())
    {
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Count $endpoint */
        $endpoint = $endpointBuilder('Count');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']              = (list) A comma-separated list of indices to restrict the results
     *        ['type']               = (list) A comma-separated list of types to restrict the results
     *        ['id']                 = (string) ID of document
     *        ['ignore_unavailable'] = (boolean) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['preference']         = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['routing']            = (string) Specific routing value
     *        ['allow_no_indices']   = (boolean) Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
     *        ['body']               = (array) A query to restrict the results (optional)
     *        ['ignore_unavailable'] = (bool) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['percolate_index']    = (string) The index to count percolate the document into. Defaults to index.
     *        ['expand_wildcards']   = (enum) Whether to expand wildcard expression to concrete indices that are open, closed or both.
     *        ['version']            = (number) Explicit version number for concurrency control
     *        ['version_type']       = (enum) Specific version type
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function countPercolate($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type  = $this->extractArgument($params, 'type');
        $id    = $this->extractArgument($params, 'id');
        $body  = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\CountPercolate $endpoint */
        $endpoint = $endpointBuilder('CountPercolate');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setID($id)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']        = (string) The name of the index with a registered percolator query (Required)
     *        ['type']         = (string) The document type (Required)
     *        ['prefer_local'] = (boolean) With `true`, specify that a local shard should be used if available, with `false`, use a random shard (default: true)
     *        ['body']         = (array) The document (`doc`) to percolate against registered queries; optionally also a `query` to limit the percolation to specific registered queries
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function percolate($params)
    {
        $index = $this->extractArgument($params, 'index');
        $type  = $this->extractArgument($params, 'type');
        $id    = $this->extractArgument($params, 'id');
        $body  = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Percolate $endpoint */
        $endpoint = $endpointBuilder('Percolate');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setID($id)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']              = (string) Default index for items which don't provide one
     *        ['type']               = (string) Default document type for items which don't provide one
     *        ['ignore_unavailable'] = (boolean) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['allow_no_indices']   = (boolean) Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
     *        ['expand_wildcards']   = (enum) Whether to expand wildcard expression to concrete indices that are open, closed or both.
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function mpercolate($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type = $this->extractArgument($params, 'type');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\MPercolate $endpoint */
        $endpoint = $endpointBuilder('MPercolate');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']            = (string) Default index for items which don't provide one
     *        ['type']             = (string) Default document type for items which don't provide one
     *        ['term_statistics']  = (boolean) Specifies if total term frequency and document frequency should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['field_statistics'] = (boolean) Specifies if document count, sum of document frequencies and sum of total term frequencies should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['fields']           = (list) A comma-separated list of fields to return. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['offsets']          = (boolean) Specifies if term offsets should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['positions']        = (boolean) Specifies if term positions should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['payloads']         = (boolean) Specifies if term payloads should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['preference']       = (string) Specify the node or shard the operation should be performed on (default: random) .Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['routing']          = (string) Specific routing value. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['parent']           = (string) Parent id of documents. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['realtime']         = (boolean) Specifies if request is real-time as opposed to near-real-time (default: true).
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function termvector($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type  = $this->extractArgument($params, 'type');
        $id    = $this->extractArgument($params, 'id');
        $body  = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\TermVector $endpoint */
        $endpoint = $endpointBuilder('TermVector');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setID($id)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * Redirect to termvector, this is just a naming difference depending on version
     */
    public function termvectors($params = array())
    {
        return $this->termvector($params);
    }
 
    /**
     * $params['index']            = (string) Default index for items which don't provide one
     *        ['type']             = (string) Default document type for items which don't provide one
     *        ['ids']              = (list) A comma-separated list of documents ids. You must define ids as parameter or set \"ids\" or \"docs\" in the request body
     *        ['term_statistics']  = (boolean) Specifies if total term frequency and document frequency should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['field_statistics'] = (boolean) Specifies if document count, sum of document frequencies and sum of total term frequencies should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['fields']           = (list) A comma-separated list of fields to return. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['offsets']          = (boolean) Specifies if term offsets should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['positions']        = (boolean) Specifies if term positions should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\"."
     *        ['payloads']         = (boolean) Specifies if term payloads should be returned. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['preference']       = (string) Specify the node or shard the operation should be performed on (default: random) .Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['routing']          = (string) Specific routing value. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['parent']           = (string) Parent id of documents. Applies to all returned documents unless otherwise specified in body \"params\" or \"docs\".
     *        ['realtime']         = (boolean) Specifies if request is real-time as opposed to near-real-time (default: true).
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function mtermvectors($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type  = $this->extractArgument($params, 'type');
        $body  = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\MTermVectors $endpoint */
        $endpoint = $endpointBuilder('MTermVectors');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']         = (string) The document ID (Required)
     *        ['index']      = (string) The name of the index (Required)
     *        ['type']       = (string) The type of the document (use `_all` to fetch the first document matching the ID across all types) (Required)
     *        ['parent']     = (string) The ID of the parent document
     *        ['preference'] = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['realtime']   = (boolean) Specify whether to perform the operation in realtime or search mode
     *        ['refresh']    = (boolean) Refresh the shard containing the document before performing the operation
     *        ['routing']    = (string) Specific routing value
     *
     * @param $params array Associative array of parameters
     *
     * @return array | boolean
     */
    public function exists($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        //manually make this verbose so we can check status code
        $params['client']['verbose'] = true;
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Exists $endpoint */
        $endpoint = $endpointBuilder('Exists');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type);
        $endpoint->setParams($params);
 
        return BooleanRequestWrapper::performRequest($endpoint);
    }
 
    /**
     * $params['id']                     = (string) The document ID (Required)
     *        ['index']                  = (string) The name of the index (Required)
     *        ['type']                   = (string) The type of the document (use `_all` to fetch the first document matching the ID across all types) (Required)
     *        ['boost_terms']            = (number) The boost factor
     *        ['max_doc_freq']           = (number) The word occurrence frequency as count: words with higher occurrence in the corpus will be ignored
     *        ['max_query_terms']        = (number) The maximum query terms to be included in the generated query
     *        ['max_word_len']           = (number) The minimum length of the word: longer words will be ignored
     *        ['min_doc_freq']           = (number) The word occurrence frequency as count: words with lower occurrence in the corpus will be ignored
     *        ['min_term_freq']          = (number) The term frequency as percent: terms with lower occurrence in the source document will be ignored
     *        ['min_word_len']           = (number) The minimum length of the word: shorter words will be ignored
     *        ['mlt_fields']             = (list) Specific fields to perform the query against
     *        ['percent_terms_to_match'] = (number) How many terms have to match in order to consider the document a match (default: 0.3)
     *        ['routing']                = (string) Specific routing value
     *        ['search_from']            = (number) The offset from which to return results
     *        ['search_indices']         = (list) A comma-separated list of indices to perform the query against (default: the index containing the document)
     *        ['search_query_hint']      = (string) The search query hint
     *        ['search_scroll']          = (string) A scroll search request definition
     *        ['search_size']            = (number) The number of documents to return (default: 10)
     *        ['search_source']          = (string) A specific search request definition (instead of using the request body)
     *        ['search_type']            = (string) Specific search type (eg. `dfs_then_fetch`, `count`, etc)
     *        ['search_types']           = (list) A comma-separated list of types to perform the query against (default: the same type as the document)
     *        ['stop_words']             = (list) A list of stop words to be ignored
     *        ['body']                   = (array) A specific search request definition
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function mlt($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Mlt $endpoint */
        $endpoint = $endpointBuilder('Mlt');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']           = (string) The name of the index
     *        ['type']            = (string) The type of the document
     *        ['fields']          = (list) A comma-separated list of fields to return in the response
     *        ['parent']          = (string) The ID of the parent document
     *        ['preference']      = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['realtime']        = (boolean) Specify whether to perform the operation in realtime or search mode
     *        ['refresh']         = (boolean) Refresh the shard containing the document before performing the operation
     *        ['routing']         = (string) Specific routing value
     *        ['body']            = (array) Document identifiers; can be either `docs` (containing full document information) or `ids` (when index and type is provided in the URL.
     *        ['_source']         = (list) True or false to return the _source field or not, or a list of fields to return
     *        ['_source_exclude'] = (list) A list of fields to exclude from the returned _source field
     *        ['_source_include'] = (list) A list of fields to extract and return from the _source field
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function mget($params = array())
    {
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Mget $endpoint */
        $endpoint = $endpointBuilder('Mget');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']       = (list) A comma-separated list of index names to use as default
     *        ['type']        = (list) A comma-separated list of document types to use as default
     *        ['search_type'] = (enum) Search operation type
     *        ['body']        = (array|string) The request definitions (metadata-search request definition pairs), separated by newlines
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function msearch($params = array())
    {
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Msearch $endpoint */
        $endpoint = $endpointBuilder('Msearch');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']        = (string) The name of the index (Required)
     *        ['type']         = (string) The type of the document (Required)
     *        ['id']           = (string) Specific document ID (when the POST method is used)
     *        ['consistency']  = (enum) Explicit write consistency setting for the operation
     *        ['parent']       = (string) ID of the parent document
     *        ['percolate']    = (string) Percolator queries to execute while indexing the document
     *        ['refresh']      = (boolean) Refresh the index after performing the operation
     *        ['replication']  = (enum) Specific replication type
     *        ['routing']      = (string) Specific routing value
     *        ['timeout']      = (time) Explicit operation timeout
     *        ['timestamp']    = (time) Explicit timestamp for the document
     *        ['ttl']          = (duration) Expiration time for the document
     *        ['version']      = (number) Explicit version number for concurrency control
     *        ['version_type'] = (enum) Specific version type
     *        ['body']         = (array) The document
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function create($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Index $endpoint */
        $endpoint = $endpointBuilder('Index');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type)
                 ->setBody($body)
                 ->createIfAbsent();
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']       = (string) Default index for items which don't provide one
     *        ['type']        = (string) Default document type for items which don't provide one
     *        ['consistency'] = (enum) Explicit write consistency setting for the operation
     *        ['refresh']     = (boolean) Refresh the index after performing the operation
     *        ['replication'] = (enum) Explicitly set the replication type
     *        ['body']        = (string) Default document type for items which don't provide one
     *        ['fields']      = (list) Default comma-separated list of fields to return in the response for updates
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function bulk($params = array())
    {
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Bulk $endpoint */
        $endpoint = $endpointBuilder('Bulk');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']        = (string) The name of the index (Required)
     *        ['type']         = (string) The type of the document (Required)
     *        ['id']           = (string) Specific document ID (when the POST method is used)
     *        ['consistency']  = (enum) Explicit write consistency setting for the operation
     *        ['op_type']      = (enum) Explicit operation type
     *        ['parent']       = (string) ID of the parent document
     *        ['percolate']    = (string) Percolator queries to execute while indexing the document
     *        ['refresh']      = (boolean) Refresh the index after performing the operation
     *        ['replication']  = (enum) Specific replication type
     *        ['routing']      = (string) Specific routing value
     *        ['timeout']      = (time) Explicit operation timeout
     *        ['timestamp']    = (time) Explicit timestamp for the document
     *        ['ttl']          = (duration) Expiration time for the document
     *        ['version']      = (number) Explicit version number for concurrency control
     *        ['version_type'] = (enum) Specific version type
     *        ['body']         = (array) The document
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function index($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Index $endpoint */
        $endpoint = $endpointBuilder('Index');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']          = (list) A comma-separated list of index names to restrict the operation; use `_all` or empty string to perform the operation on all indices
     *        ['ignore_indices'] = (enum) When performed on multiple indices, allows to ignore `missing` ones
     *        ['preference']     = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['routing']        = (string) Specific routing value
     *        ['source']         = (string) The URL-encoded request definition (instead of using request body)
     *        ['body']           = (array) The request definition
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function suggest($params = array())
    {
        $index = $this->extractArgument($params, 'index');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Suggest $endpoint */
        $endpoint = $endpointBuilder('Suggest');
        $endpoint->setIndex($index)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']                       = (string) The document ID (Required)
     *        ['index']                    = (string) The name of the index (Required)
     *        ['type']                     = (string) The type of the document (Required)
     *        ['analyze_wildcard']         = (boolean) Specify whether wildcards and prefix queries in the query string query should be analyzed (default: false)
     *        ['analyzer']                 = (string) The analyzer for the query string query
     *        ['default_operator']         = (enum) The default operator for query string query (AND or OR)
     *        ['df']                       = (string) The default field for query string query (default: _all)
     *        ['fields']                   = (list) A comma-separated list of fields to return in the response
     *        ['lenient']                  = (boolean) Specify whether format-based query failures (such as providing text to a numeric field) should be ignored
     *        ['lowercase_expanded_terms'] = (boolean) Specify whether query terms should be lowercased
     *        ['parent']                   = (string) The ID of the parent document
     *        ['preference']               = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['q']                        = (string) Query in the Lucene query string syntax
     *        ['routing']                  = (string) Specific routing value
     *        ['source']                   = (string) The URL-encoded query definition (instead of using the request body)
     *        ['_source']                  = (list) True or false to return the _source field or not, or a list of fields to return
     *        ['_source_exclude']          = (list) A list of fields to exclude from the returned _source field
     *        ['_source_include']          = (list) A list of fields to extract and return from the _source field
     *        ['body']                     = (string) The URL-encoded query definition (instead of using the request body)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function explain($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Explain $endpoint */
        $endpoint = $endpointBuilder('Explain');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']                    = (list) A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices
     *        ['type']                     = (list) A comma-separated list of document types to search; leave empty to perform the operation on all types
     *        ['analyzer']                 = (string) The analyzer to use for the query string
     *        ['analyze_wildcard']         = (boolean) Specify whether wildcard and prefix queries should be analyzed (default: false)
     *        ['default_operator']         = (enum) The default operator for query string query (AND or OR)
     *        ['df']                       = (string) The field to use as default where no field prefix is given in the query string
     *        ['explain']                  = (boolean) Specify whether to return detailed information about score computation as part of a hit
     *        ['fields']                   = (list) A comma-separated list of fields to return as part of a hit
     *        ['from']                     = (number) Starting offset (default: 0)
     *        ['ignore_indices']           = (enum) When performed on multiple indices, allows to ignore `missing` ones
     *        ['indices_boost']            = (list) Comma-separated list of index boosts
     *        ['lenient']                  = (boolean) Specify whether format-based query failures (such as providing text to a numeric field) should be ignored
     *        ['lowercase_expanded_terms'] = (boolean) Specify whether query terms should be lowercased
     *        ['preference']               = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['q']                        = (string) Query in the Lucene query string syntax
     *        ['routing']                  = (list) A comma-separated list of specific routing values
     *        ['scroll']                   = (duration) Specify how long a consistent view of the index should be maintained for scrolled search
     *        ['search_type']              = (enum) Search operation type
     *        ['size']                     = (number) Number of hits to return (default: 10)
     *        ['sort']                     = (list) A comma-separated list of <field>:<direction> pairs
     *        ['source']                   = (string) The URL-encoded request definition using the Query DSL (instead of using request body)
     *        ['_source']                  = (list) True or false to return the _source field or not, or a list of fields to return
     *        ['_source_exclude']          = (list) A list of fields to exclude from the returned _source field
     *        ['_source_include']          = (list) A list of fields to extract and return from the _source field
     *        ['stats']                    = (list) Specific 'tag' of the request for logging and statistical purposes
     *        ['suggest_field']            = (string) Specify which field to use for suggestions
     *        ['suggest_mode']             = (enum) Specify suggest mode
     *        ['suggest_size']             = (number) How many suggestions to return in response
     *        ['suggest_text']             = (text) The source text for which the suggestions should be returned
     *        ['timeout']                  = (time) Explicit operation timeout
     *        ['version']                  = (boolean) Specify whether to return document version as part of a hit
     *        ['body']                     = (array|string) The search definition using the Query DSL
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function search($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type = $this->extractArgument($params, 'type');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Search $endpoint */
        $endpoint = $endpointBuilder('Search');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']                    = (list) A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices
     *        ['type']                     = (list) A comma-separated list of document types to search; leave empty to perform the operation on all types
     *        ['analyzer']                 = (string) The analyzer to use for the query string
     *        ['analyze_wildcard']         = (boolean) Specify whether wildcard and prefix queries should be analyzed (default: false)
     *        ['default_operator']         = (enum) The default operator for query string query (AND or OR)
     *        ['df']                       = (string) The field to use as default where no field prefix is given in the query string
     *        ['explain']                  = (boolean) Specify whether to return detailed information about score computation as part of a hit
     *        ['fields']                   = (list) A comma-separated list of fields to return as part of a hit
     *        ['from']                     = (number) Starting offset (default: 0)
     *        ['ignore_indices']           = (enum) When performed on multiple indices, allows to ignore `missing` ones
     *        ['indices_boost']            = (list) Comma-separated list of index boosts
     *        ['lenient']                  = (boolean) Specify whether format-based query failures (such as providing text to a numeric field) should be ignored
     *        ['lowercase_expanded_terms'] = (boolean) Specify whether query terms should be lowercased
     *        ['preference']               = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['q']                        = (string) Query in the Lucene query string syntax
     *        ['routing']                  = (list) A comma-separated list of specific routing values
     *        ['scroll']                   = (duration) Specify how long a consistent view of the index should be maintained for scrolled search
     *        ['search_type']              = (enum) Search operation type
     *        ['size']                     = (number) Number of hits to return (default: 10)
     *        ['sort']                     = (list) A comma-separated list of <field>:<direction> pairs
     *        ['source']                   = (string) The URL-encoded request definition using the Query DSL (instead of using request body)
     *        ['_source']                  = (list) True or false to return the _source field or not, or a list of fields to return
     *        ['_source_exclude']          = (list) A list of fields to exclude from the returned _source field
     *        ['_source_include']          = (list) A list of fields to extract and return from the _source field
     *        ['stats']                    = (list) Specific 'tag' of the request for logging and statistical purposes
     *        ['suggest_field']            = (string) Specify which field to use for suggestions
     *        ['suggest_mode']             = (enum) Specify suggest mode
     *        ['suggest_size']             = (number) How many suggestions to return in response
     *        ['suggest_text']             = (text) The source text for which the suggestions should be returned
     *        ['timeout']                  = (time) Explicit operation timeout
     *        ['version']                  = (boolean) Specify whether to return document version as part of a hit
     *        ['body']                     = (array|string) The search definition using the Query DSL
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function searchExists($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type = $this->extractArgument($params, 'type');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\SearchExists $endpoint */
        $endpoint = $endpointBuilder('SearchExists');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']              = (list) A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices
     *        ['type']               = (list) A comma-separated list of document types to search; leave empty to perform the operation on all types
     *        ['preference']         = (string) Specify the node or shard the operation should be performed on (default: random)
     *        ['routing']            = (string) Specific routing value
     *        ['local']              = (bool) Return local information, do not retrieve the state from master node (default: false)
     *        ['ignore_unavailable'] = (bool) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['allow_no_indices']   = (bool) Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
     *        ['expand_wildcards']   = (enum) Whether to expand wildcard expression to concrete indices that are open, closed or both.
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function searchShards($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type = $this->extractArgument($params, 'type');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\SearchShards $endpoint */
        $endpoint = $endpointBuilder('SearchShards');
        $endpoint->setIndex($index)
                 ->setType($type);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']                    = (list) A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices
     *        ['type']                     = (list) A comma-separated list of document types to search; leave empty to perform the operation on all types
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function searchTemplate($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $type = $this->extractArgument($params, 'type');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Search $endpoint */
        $endpoint = $endpointBuilder('SearchTemplate');
        $endpoint->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['scroll_id'] = (string) The scroll ID for scrolled search
     *        ['scroll']    = (duration) Specify how long a consistent view of the index should be maintained for scrolled search
     *        ['body']      = (string) The scroll ID for scrolled search
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function scroll($params = array())
    {
        $scrollID = $this->extractArgument($params, 'scroll_id');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Scroll $endpoint */
        $endpoint = $endpointBuilder('Scroll');
        $endpoint->setScrollID($scrollID)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['scroll_id'] = (string) The scroll ID for scrolled search
     *        ['scroll']    = (duration) Specify how long a consistent view of the index should be maintained for scrolled search
     *        ['body']      = (string) The scroll ID for scrolled search
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function clearScroll($params = array())
    {
        $scrollID = $this->extractArgument($params, 'scroll_id');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Scroll $endpoint */
        $endpoint = $endpointBuilder('Scroll');
        $endpoint->setScrollID($scrollID)
                 ->setBody($body)
                 ->setClearScroll(true);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']                = (string) Document ID (Required)
     *        ['index']             = (string) The name of the index (Required)
     *        ['type']              = (string) The type of the document (Required)
     *        ['consistency']       = (enum) Explicit write consistency setting for the operation
     *        ['fields']            = (list) A comma-separated list of fields to return in the response
     *        ['lang']              = (string) The script language (default: mvel)
     *        ['parent']            = (string) ID of the parent document
     *        ['percolate']         = (string) Perform percolation during the operation; use specific registered query name, attribute, or wildcard
     *        ['refresh']           = (boolean) Refresh the index after performing the operation
     *        ['replication']       = (enum) Specific replication type
     *        ['retry_on_conflict'] = (number) Specify how many times should the operation be retried when a conflict occurs (default: 0)
     *        ['routing']           = (string) Specific routing value
     *        ['script']            = () The URL-encoded script definition (instead of using request body)
     *        ['timeout']           = (time) Explicit operation timeout
     *        ['timestamp']         = (time) Explicit timestamp for the document
     *        ['ttl']               = (duration) Expiration time for the document
     *        ['version_type']      = (number) Explicit version number for concurrency control
     *        ['body']              = (array) The request definition using either `script` or partial `doc`
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function update($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        $index = $this->extractArgument($params, 'index');
 
        $type = $this->extractArgument($params, 'type');
 
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Update $endpoint */
        $endpoint = $endpointBuilder('Update');
        $endpoint->setID($id)
                 ->setIndex($index)
                 ->setType($type)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']   = (string) The script ID (Required)
     *        ['lang'] = (string) The script language (Required)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function getScript($params)
    {
        $id = $this->extractArgument($params, 'id');
        $lang = $this->extractArgument($params, 'lang');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Script\Get $endpoint */
        $endpoint = $endpointBuilder('Script\Get');
        $endpoint->setID($id)
                 ->setLang($lang);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']   = (string) The script ID (Required)
     *        ['lang'] = (string) The script language (Required)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function deleteScript($params)
    {
        $id = $this->extractArgument($params, 'id');
        $lang = $this->extractArgument($params, 'lang');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Script\Delete $endpoint */
        $endpoint = $endpointBuilder('Script\Delete');
        $endpoint->setID($id)
                 ->setLang($lang);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']   = (string) The script ID (Required)
     *        ['lang'] = (string) The script language (Required)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function putScript($params)
    {
        $id   = $this->extractArgument($params, 'id');
        $lang = $this->extractArgument($params, 'lang');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Script\Put $endpoint */
        $endpoint = $endpointBuilder('Script\Put');
        $endpoint->setID($id)
                 ->setLang($lang)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']   = (string) The search template ID (Required)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function getTemplate($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Template\Get $endpoint */
        $endpoint = $endpointBuilder('Template\Get');
        $endpoint->setID($id);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']   = (string) The search template ID (Required)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function deleteTemplate($params)
    {
        $id = $this->extractArgument($params, 'id');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Template\Delete $endpoint */
        $endpoint = $endpointBuilder('Template\Delete');
        $endpoint->setID($id);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']   = (string) The search template ID (Required)
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function putTemplate($params)
    {
        $id   = $this->extractArgument($params, 'id');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\Template\Put $endpoint */
        $endpoint = $endpointBuilder('Template\Put');
        $endpoint->setID($id)
                 ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['index']              = (list) A comma-separated list of indices to restrict the results
     *        ['fields']             = (list) A comma-separated list of fields for to get field statistics for (min value, max value, and more)
     *        ['level']              = (enum) Defines if field stats should be returned on a per index level or on a cluster wide level
     *        ['ignore_unavailable'] = (bool) Whether specified concrete indices should be ignored when unavailable (missing or closed)
     *        ['allow_no_indices']   = (bool) Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)
     *        ['expand_wildcards']   = (enum) Whether to expand wildcard expression to concrete indices that are open, closed or both.
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function fieldStats($params = array())
    {
        $index = $this->extractArgument($params, 'index');
        $body = $this->extractArgument($params, 'body');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\FieldStats $endpoint */
        $endpoint = $endpointBuilder('FieldStats');
        $endpoint->setIndex($index)
            ->setBody($body);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
 
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * $params['id']                 = (string) ID of the template to render
     *
     * @param $params array Associative array of parameters
     *
     * @return array
     */
    public function renderSearchTemplate($params = array())
    {
        $body = $this->extractArgument($params, 'body');
        $id   = $this->extractArgument($params, 'id');
 
        /** @var callback $endpointBuilder */
        $endpointBuilder = $this->endpoints;
 
        /** @var \Elasticsearch\Endpoints\RenderSearchTemplate $endpoint */
        $endpoint = $endpointBuilder('RenderSearchTemplate');
        $endpoint->setBody($body)
            ->setID($id);
        $endpoint->setParams($params);
        $response = $endpoint->performRequest();
        return $endpoint->resultOrFuture($response);
    }
 
    /**
     * Operate on the Indices Namespace of commands
     *
     * @return IndicesNamespace
     */
    public function indices()
    {
        return $this->indices;
    }
 
    /**
     * Operate on the Cluster namespace of commands
     *
     * @return ClusterNamespace
     */
    public function cluster()
    {
        return $this->cluster;
    }
 
    /**
     * Operate on the Nodes namespace of commands
     *
     * @return NodesNamespace
     */
    public function nodes()
    {
        return $this->nodes;
    }
 
    /**
     * Operate on the Snapshot namespace of commands
     *
     * @return SnapshotNamespace
     */
    public function snapshot()
    {
        return $this->snapshot;
    }
 
    /**
     * Operate on the Cat namespace of commands
     *
     * @return CatNamespace
     */
    public function cat()
    {
        return $this->cat;
    }
 
    /**
     * @param array $params
     * @param string $arg
     *
     * @return null|mixed
     */
    public function extractArgument(&$params, $arg)
    {
        if (is_object($params) === true) {
            $params = (array) $params;
        }
 
        if (isset($params[$arg]) === true) {
            $val = $params[$arg];
            unset($params[$arg]);
 
            return $val;
        } else {
            return null;
        }
    }
 
    private function verifyNotNullOrEmpty($name, $var) {
        if ($var === null) {
            throw new InvalidArgumentException("$name cannot be null.");
        }
 
        if (is_string($var)) {
            if (strlen($var) === 0) {
                throw new InvalidArgumentException("$name cannot be an empty string");
            }
        }
 
        if (is_array($var)) {
            if (strlen(implode("", $var)) === 0) {
                throw new InvalidArgumentException("$name cannot be an array of empty strings");
            }
        }
    }
}
#9Elasticsearch\Client->search(Array([index] => partsit, [type] => parts, [body] => Array([query] => Array([query_string] => Array()), [filter] => Array([and] => Array()))))
/home/ubuntu/partsit-site/app/models/Parts.php (1356)
<?php
 
namespace Models;
 
/**
 * An encapsulation of a part on PartsIT.
 * 
 * PHP Version 5
 *
 * @category Models
 * @package Models
 * @author Jack Timblin <jacktimblin@gmail.com>
 * @copyright 2015 PartsIT.com
 * @license GNU General Public License V3.0
 * @version Release: 1
 * @see \Models\BaseModel, \Phalcon\Mvc\Model, \Phalcon\Mvc\Model\Behavior\Timestampable
 * @since Release: 1
 */
class Parts extends BaseModel
{
  /* the unique partID of this part
     @var int
   */
  protected $partID;
 
  /* the the manufacturers partNumber for this part.
     @var string
   */
  protected $partNumber;
 
  /* the ID of the manufacturer for this part.
     @var int
   */
  protected $manufacturerID;
 
  /* the short title/description string for this part.
     @var string
   */
  protected $title;
 
  /* the unique url for this part, used to 
     have dynamic product pages.
     @var string
   */
  protected $partURL;
 
  /* the short description for this part.
     @var string
   */
  protected $shortDescription;
 
  /* the category that this part belongs to.
     @var int
   */
  protected $subCategoryID;
 
  /* the date this part was added to the system.
     @var string
   */
  protected $dateCreated;
 
  /* the date this part was last updated
     to the system.
     @var string
   */
  protected $dateModified;
 
  /* The International Article Number (E(uropean)AN) for this part.
     Used to uniquely identify this part on Amazon, Icecat etc.
     @var string
   */
  protected $ean;
 
  /**
   * Called in the models manager, sets up default variables and relationships
   * for this model.
   * @return void
   * @access public
   * @see \Phalcon\Mvc\Model::initialize()
   */
  public function initialize()
  {
    $this->addBehavior(
      new \Phalcon\Mvc\Model\Behavior\Timestampable(
        array(
          'beforeCreate' => array(
            'field'  => 'dateCreated',
            'format' => 'Y-m-d H:i:s'
          ),
          'beforeUpdate' => array(
            'field'  => 'dateModified',
            'format' => 'Y-m-d H:i:s'
          )
        )
      )
    );
    
    $this->useDynamicUpdate(true);
    
    $this->hasOne(
      'partID', 
      '\Models\PromoParts', 
      'partID', 
      array(
        'alias' => 'PromoPart'
      )
    );
 
    $this->hasOne(
      'partID', 
      '\Models\CustomPromoParts', 
      'partID', 
      array(
        'alias' => 'CustomPromoParts'
      )
    );
 
    $this->hasMany(
      'partID', 
      '\Models\PartFeatureImages', 
      'partID', 
      array(
        'alias' => 'FeatureImages'
      )
    );
 
    $this->hasOne(
      'partID', 
      '\Models\IcecatLastUpdated', 
      'partID', 
      array(
        'alias' => 'IcecatLastUpdated'
      )
    );
 
    $this->hasMany(
      'partID',
      '\Models\PartTechSpecs',
      'partID',
      array(
        'alias' => 'PartTechSpecs'
      )
    );
    
    
    $this->hasMany(
      'partID',
      '\Models\PartBundles',
      'partID',
      array(
        'alias' => 'PartBundles'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\PriceRequests',
      'partID',
      array(
        'alias' => 'PriceRequests'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\PartVideos',
      'partID',
      array(
        'alias' => 'PartVideos'
      )
    );
    
    $this->belongsTo(
      'subCategoryID',
      '\Models\SubCategories',
      'subCategoryID',
      array(
        'alias' => 'SubCategories'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\DeliveryParts',
      'partNumber',
      array(
        'alias' => 'SapDeliveryParts'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\RecentlyViewed',
      'partID',
      array(
        'alias' => 'RecentlyViewed'
      )
    );
    
    $this->belongsTo(
      'manufacturerID',
      '\Models\Manufacturers',
      'manufacturerID',
      array(
        'alias' => 'Manufacturers'
      )
    );
    
    $this->hasOne(
      'partID',
      '\Models\PartDescriptions',
      'partID',
      array(
        'foreignKey' => array(
          'message' => 'The part has a part description.'
        ),
        'alias' => 'PartDescriptions'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\PartFeatures',
      'partID',
      array(
        'foreignKey' => array(
          'message' => 'The part has a part feature.'
        ),
        'alias' => 'PartFeatures'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\Reviews',
      'partID',
      array(
        'foreignKey' => array(
          'message' => 'The part belongs to a review.'
        ),
        'alias' => 'Reviews'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\PartImages',
      'partID',
      array(
        'alias' => 'PartImages'
      )
    );
    
    $this->hasOne(
      'partID',
      '\Models\FeaturedReviews',
      'partID',
      array(
        'alias' => 'FeaturedReviews'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\StockParts',
      'partID',
      array(
        'foreignKey' => array(
          'message' => 'The part has a stock part.'
        ),
        'alias' => 'StockParts'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\CompatibleParts',
      'partID',
      array(
        'foreignKey' => array(
          'message' => 'The part has a compatible part.'
        ),
        'alias' => 'CompatibleParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\OrderParts',
      'partNumber',
      array(
        'alias' => 'SapOrderParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\QuoteParts',
      'partNumber',
      array(
        'alias' => 'SapQuoteParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\PurchaseOrderParts',
      'partNumber',
      array(
        'alias' => 'SapPurchaseOrderParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\ArInvoiceParts',
      'partNumber',
      array(
        'alias' => 'SapArInvoiceParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\ApInvoiceParts',
      'partNumber',
      array(
        'alias' => 'SapApInvoiceParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\CustomerRmaParts',
      'partNumber',
      array(
        'alias' => 'SapCustomerRmaParts'
      )
    );
    
    $this->hasMany(
      'partNumber',
      '\Models\SAP\SupplierRmaParts',
      'partNumber',
      array(
        'alias' => 'SapSupplierRmaParts'
      )
    );
    
    $this->hasMany(
      'partID',
      '\Models\PartKeywords',
      'partID',
      array(
        'alias' => 'PartKeywords'
      )
    );
    
    $this->hasOne(
      'partID',
      '\Models\HotProducts',
      'partID',
      array(
        'alias' => 'HotProducts'
      )
    );
 
    $this->hasMany(
      'partID', 
      '\Models\BundleParts', 
      'partID', 
      array(
        'alias' => 'BundleParts'
      )
    );
 
    $this->hasMany(
      'partID', 
      '\Models\Images', 
      'partID', 
      array(
        'alias' => 'Images', 
        'foreignKey' => array(
          'action' => \Phalcon\Mvc\Model\Relation::ACTION_CASCADE
        )
      )
    );
  }
 
  /**
   * checks to see if this part is part 
   * of a bundle promotion.
   * @return bool TRUE if this part is part of a 
   *           bundle promo, FALSE otherwise.
   * @access public
   */
  public function isPartOfABundle() 
  {
    $bpart = $this->getBundleParts();
 
    if (!$bpart) {
      return false;
    }
 
    $bundles = array();
 
    $now = new \DateTime();
    $now = $now->format('Y-m-d');
    foreach ($bpart as $bp) {
      $b = $bp->getBundle(array('validTo > :now:', 'bind' => array('now' => $now)));
      if($b instanceof \Models\Bundle) {
        $bundles[] = $b;
      }
    }
    return (count($bundles) > 0);
  }
 
  /**
   * gets the current promotion that is applied to this part.
   * @return mixed either the custom promo parts, the promo parts or null if there is 
   *          no promotion that has been applied to this part.
   * @access public
   */
  public function getPromotion() 
  {
    $c = $this->getCustomPromoParts();
    if ($c) {
      return $c;
    }
 
    return $this->getPromoPart();
 
  }
 
  /**
   * determines the bulk buy promotions for this part.
   * bulk buy promotions are only applied to stock parts.
   * @param int $quantityRequired the quantity to work out discount for.
   * @return string the bulk buy discounted price formatted to a string.
   * @access public                             
   */
  public function getBulkRate($quantityRequired) 
  {
        $price = 0;
        $discount = 0;
        $productPrice = $this->getBestPrice(array('vat' => true));
 
        $charterpoint = \Models\Distributors::findFromURL('charterpoint');
        
    $stockPart = \Models\StockParts::findFirst(
      array(
        'partID = :partID:
        AND
        distributorID = :charterpoint:',
        'bind' => array(
          'partID'       => $this->getPartID(),
          'charterpoint' => $charterpoint->getDistributorID()
        ),
        'order' => 'unitPrice ASC'
      )
    );
    
    if (!$stockPart) {
      return;
    }
        
        if ($productPrice == 0) {
            return;
        }
 
        $max = $stockPart->getQuantity();
        
        if (($quantityRequired > 1) && ($max > 1)) {
            $discount = 8;
        }
        
        if ($max <= $quantityRequired) {
      $remainder = $quantityRequired - $max;
      $price = $remainder * $productPrice;
      $discountable = $quantityRequired - $remainder;
    } else {
      $discountable = $quantityRequired;
    }
    
    $discountable = $discountable - 1;
    
    $price = $price + $productPrice;
    $price = $price + ($discountable * ($productPrice - $discount));
    
    return number_format($price, 2, ".", ""); 
    }
 
    /**
     * Gets the EAN for this part.
     * @return string the EAN for this part, 
     * or null if we do not have one saved.
     * @access public
     */
    public function getEan() 
    {
      return $this->ean;
    }
 
    /**
     * Sets the EAN for this part.
     * @param string $ean the EAN for this part
     * @return void
     * @access public
     */
    public function setEan($ean) 
    {
      $this->ean = $ean;
    }
  
  /**
   * checks to see if this parts information needs
   * updating from icecat.
   * @return bool TRUE if this part needs updating, FALSE otherwise.
   * @access public
   */
  public function partNeedsUpdating() 
  {
    $lastUpdated = $this->getIcecatLastUpdated();
    return (!($lastUpdated instanceof \Models\IcecatLastUpdated));
  }
  
  /**
   * Finds an existing part entry or generates a new one.
   * @param string $manufacturer      the manufacturer name for this part.
   * @param string $partNumber      the manufacturers part number for this part.
   * @param string $shortDescription the short description for this part.
   * @return \Models\Parts the exisiting part entry or the newly generated one.
   * @static
   * @access public
   */
  public static function createOrFind(
    $manufacturer, 
    $partNumber,
    $shortDescription)
  {
    $manufacturer = \Models\Manufacturers::createOrFind($manufacturer);
    
    $part = \Models\Parts::findFirst(
      array(
        'partNumber = :partNumber:
        AND
        manufacturerID = :manufacturerID:',
        'bind' => array(
          'partNumber'     => $partNumber,
          'manufacturerID' => $manufacturer->getManufacturerID()
        )
      )
    );
    
    if (!$part) {
      $filter = new \Phalcon\Filter();
      
      $filter->add('urlSlug',     new \PhalconHelpers\Filters\URLSlug());
      $filter->add('description', new \PhalconHelpers\Filters\Description());
      
      $partURL = $filter->sanitize($manufacturer->getManufacturerURL() . '-' . $partNumber, 'urlSlug');
      
      $shortDescription = $filter->sanitize($shortDescription, 'description');
      
      while (\Models\Parts::count('partURL = "' . $partURL . '"')) {
        //TODO
        $partURL = \Phalcon\Text::increment($partURL, '-');
      }
      
      $part = new \Models\Parts();
      
      $part->setPartNumber($partNumber);
      $part->setManufacturerID($manufacturer->getManufacturerID());
      $part->setPartURL($partURL);
      $part->setShortDescription($shortDescription);
      
      $part->save();
    }
    
    return $part;
  }
    
    /**
   * searches the elasticsearch index for part number matches.
   * @param string $partNumber  the manufacturers part number for this part.
   * @param bool   $onlyExact   [optional] whether to only accept an exact match
   *                the default is FALSE.     
   * @return array the parts matching the part number query.
   * @static
   * @access public
   */    
    public static function searchByPartNumber($partNumber, $onlyExact = false) 
    {
        
        $results = array('total' => 0, 'parts' => array());
        
        if (!isset($partNumber) || strlen($partNumber) == 0) 
        {
            return $results;
        }
 
        //first check the db for an exact match
        $ex = Parts::findFirst(
          array(
            'partNumber = ?1',
            'bind' => array(
              1 => $partNumber
            )
          )
        );
 
        if ($ex) 
        {
          return array('total' => 1, 'parts' => array($ex));
        }
 
        if ($onlyExact) 
        {
          return array('total' => 0, 'parts' => array());
        }
        
        $body = array(
            'query' => array(
                'filtered' => array(
                    'query' => array(
                        'query_string' => array(
                            'default_field' => 'partNumber',
                            'query' => $partNumber.'*'
                        )
                    ),
                    'filter' => array(
                        'type' => array(
                            'value' => 'parts'
                        )
                    )
                ) 
            )
        );
        
        $res = \Phalcon\DI::getDefault()->get('elasticSearch')->search(array('index' => 'partsit', 'type' => 'parts', 'body' => $body));
        
        $results['total'] = $res['hits']['total'];
        
        $e = array();
        
        if ($results['total'] != 0) 
        {
            foreach ($res['hits']['hits'] as $entry) 
            {
                if (!in_array($entry['_id'], $e)) 
                {
                    $results['parts'][] = \Models\Parts::findFirst(
                        array(
                            'partID = ?1',
                            'bind' => array(
                                1 => $entry['_id']
                            )
                        )
                    );
                    $e[] = $entry['_id'];
                }
            }
        }
        
        return $results;
    }
  
  /**
   * searches for a part with the provided URL.
   * @param string $partURL     the url to search for.    
   * @return \Model\Parts|bool  the found parts model on success, FALSE otherwise.
   * @static
   * @access public
   */    
  public static function findFromURL($partURL)
  {
    $part = \Models\Parts::findFirst(
      array(
        'partURL = :partURL:',
        'bind' => array(
          'partURL' => $partURL
        )
      )
    );
    
    return $part;
  }
 
  /**
   * determines the bulk buy options for a particular part.
   * @return array
   * @access public
   */
  public function getBulkBuyOptions() {
    //determine the bulk buy options for this part.
        $distro = \Models\Distributors::findFromURL('charterpoint');
        
        $opts = array();
        if ($distro instanceof \Models\Distributors
                && $this->countStockParts(array(
                    'distributorID = ' . $distro->getDistributorID()
                )) > 0) 
        {
            
            $stockParts = $this->getStockParts(array(
                'distributorID = ' . $distro->getDistributorID()
            ));
            
            $stockParts->setHydrateMode(\Phalcon\Mvc\Model\Resultset::HYDRATE_ARRAYS);
            
            $sp = $stockParts[0];
            
            if ($sp['quantity'] > 20) 
            {
                $opts = array(
                    1 => $this->getBulkRate(1),
                    2 => $this->getBulkRate(2),
                    5 => $this->getBulkRate(5),
                    10 => $this->getBulkRate(10),
                    20 => $this->getBulkRate(20)
                );
            } else {
                for ($i = 1; $i <= 5; $i++) 
                {
                    $opts[$i] = $this->getBulkRate($i);
                }
            }
        }
 
        if(count($opts) == 0) {
          return;
        }
 
        return $opts;
  }
  
  /**
   * Returns the parts that have been most ordered according to SAP.
   * @param int $limit the max amount of parts to return.
   * @return array an array containing the most ordered parts with a count up to the $limit.
   * @static
   * @access public
   */
  public static function findMostOrdered($limit)
  {
    $partIDs = \Phalcon\DI::getDefault()->get('modelsManager')->executeQuery(
      'SELECT
        \Models\SAP\Parts.partID,
        COUNT(\Models\SAP\OrderParts.orderID)
      FROM \Models\SAP\Parts
      
      JOIN \Models\SAP\OrderParts ON
        \Models\SAP\Parts.partNumber = \Models\SAP\OrderParts.partNumber
 
      JOIN \Models\SAP\Orders ON \Models\SAP\OrderParts.orderID = \Models\SAP\Orders.orderID
      
      WHERE
        \Models\SAP\Parts.partID IS NOT NULL
 
      AND 
        \Models\SAP\Orders.shortCode LIKE "C-PARTS%"
      
      GROUP BY
        \Models\SAP\Parts.partID
      
      ORDER BY
        COUNT(\Models\SAP\OrderParts.orderID) DESC
      
      LIMIT ' . intval($limit)
    );
    
    $mostOrderedParts = array();
 
    foreach ($partIDs as $partID) 
    {
      $part = \Models\Parts::findFirst(
        array(
          'partID = :partID:',
          'bind' => array(
            'partID' => $partID->partID
          )
        )
      );
      $mostOrderedParts[] = $part;
    }
    
    return $mostOrderedParts;
  }
  
  /**
   * Returns the parts that have been most recently ordered according to SAP.
   * @param int $limit the max amount of parts to return.
   * @return array an array containing the most recently ordered 
   *          parts with a count up to the $limit.
   * @static
   * @access public
   */
  public static function findRecentlyOrdered($limit)
  {
    $partIDs = \Phalcon\DI::getDefault()->get('modelsManager')->executeQuery(
      'SELECT DISTINCT
        \Models\SAP\Parts.partID AS partID
      FROM \Models\SAP\Parts
      
      JOIN \Models\SAP\OrderParts ON
        \Models\SAP\Parts.partNumber = \Models\SAP\OrderParts.partNumber
      
      WHERE
        \Models\SAP\Parts.partID IS NOT NULL
      
      ORDER BY
        \Models\SAP\OrderParts.orderID DESC
      
      LIMIT ' . intval($limit)
    );
    
    $recentlyOrderedParts = array();
    
    foreach ($partIDs as $partID) 
    {
      $part = \Models\Parts::findFirst(
        array(
          'partID = :partID:',
          'bind' => array(
            'partID' => $partID->partID
          )
        )
      );
 
      if ($part) 
      {
        $recentlyOrderedParts[] = $part;
      }
    }
    
    return $recentlyOrderedParts;
  }
  
  /**
   * After save event triggered after a model is persisted to the database.
   * Creates a corresponding elasticsearch index entry for the saved part model.
   * @return void
   * @access public
   */
  public function afterSave()
  {
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $quantity  = \Models\StockParts::sum(
      array(
        'partID = :partID:',
        'bind' => array(
          'partID' => $this->getPartID()
        ),
        'column' => 'quantity'
      )
    );
    
    $body = array(
      'manufacturer'     => $this->getManufacturers()->getManufacturer(),
      'partNumber'       => $this->getPartNumber(),
      'shortDescription' => $this->getShortDescription(),
      'partURL'          => $this->getPartURL(),
      
      'manufacturerID'   => $this->getManufacturers()->getManufacturerID(),
      
      'unitPrice'        => $this->getBestPrice(),
      'quantity'         => $quantity,
      
      'stockParts'       => array()
    );
    
    $title = $this->getTitle();
    
    if ($title != $this->getPartNumber()) 
    {
      $body['title'] = $title;
    }
    
    $partDescription = $this->getPartDescriptions();
    
    if ($partDescription) 
    {
      //TODO Strip HTML.
      $body['longDescription'] = $partDescription->getDescription();
    }
    
    $subCategory = $this->getSubCategories();
    
    if ($subCategory) 
    {
      $category = $subCategory->getCategories();
      $section  = $category->getSections();
      
      $body['category'] = array(
        'section'     => $section->getSection(),
        'category'    => $category->getCategory(),
        'subCategory' => $subCategory->getSubCategory()
      );
      
      $body['sectionID']     = $section->getSectionID();
      $body['categoryID']    = $category->getCategoryID();
      $body['subCategoryID'] = $subCategory->getSubCategoryID();
    }
    
    $compatibleParts = $this->getCompatibleParts();
    
    if ($compatibleParts && (count($compatibleParts) > 0)) 
    {
      $body['partFinder'] = array();
      
      foreach ($compatibleParts as $compatiblePart) 
      {
        $modelNumber        = $compatiblePart->getModelNumbers();
        $modelFamily        = $modelNumber->getModelFamilies();
        $systemManufacturer = $modelFamily->getSystemManufacturers();
        
        $body['partFinder'][] = array(
          'systemManufacturer' => $systemManufacturer->getSystemManufacturer(),
          'modelFamily'        => $modelFamily->getModelFamily(),
          'modelNumber'        => $modelNumber->getModelNumber(),
          
          'systemManufacturerID' => $systemManufacturer->getSystemManufacturerID(),
          'modelFamilyID'        => $modelFamily->getModelFamilyID(),
          'modelNumberID'        => $modelNumber->getModelNumberID()
        );
      }
    }
    
    $partFeatures = $this->getPartFeatures();
    
    if ($partFeatures && (count($partFeatures) > 0)) 
    {
      $body['partFeatures'] = array();
      
      foreach ($partFeatures as $partFeature) 
      {
        $body['partFeatures'][] = array(
          $partFeature->getFeature()
        );
      }
    }
    
    $stockParts = $this->getStockParts();
    
    foreach ($stockParts as $stockPart) {
      $body['stockParts'][] = array(
        'distributorID'     => $stockPart->getDistributorID(),
        'partConditionID'   => $stockPart->getPartConditionID(),
        
        'unitPrice'         => $stockPart->getUnitPrice(),
        'quantity'          => $stockPart->getQuantity(),
        
        'estimatedLeadTime' => $stockPart->getEstimatedLeadTime(),
        
        'lastUpdated'       => $stockPart->getLastUpdated(),
        'validUntil'        => $stockPart->getValidUntil(),
        
        'url'               => $stockPart->getURL()
      );
    }
    try {
      $params = array(
        'index' => 'partsit',
        'type'  => 'parts',
        'id'    => $this->getPartID(),
        'body'  => $body,
        
      );
      if(class_exists('\Elasticsearch\ClientBuilder')) {
        $params['client'] = array();
        $params['client']['ignore'] = array(400, 404);
      } else {
        $params['ignore'] = array(400, 404);
      }
      $elasticSearch->index(
        $params
      );
    } catch (\Elasticsearch\Common\Exceptions\Serializer\JsonSerializationError $e) {
      return;
    } catch (\Exception $e) {
      //nothing, just continue to next
    }
  }
  
  /**
   * Before delete event triggered before a model is removed from the database.
   * removes the elasticsearch index entry for this part on part removal.
   * @return void
   * @access public
   */
  public function beforeDelete()
  {
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    try {
      $elasticSearch->delete(
        array(
          'index' => 'partsit',
          'type'  => 'parts',
          'id'    => $this->getPartID()
        )
      );
    } catch (\Elasticsearch\Common\Exceptions\Missing404Exception $e) { }
    
    return true;
  }
 
  /**
   * generates an elastic search query. This function does not execute the query.
   * @param array $parameters an array of parameters for this query, search term, filters etc.
   * @return array the constructed elastic search query.
   * @static
   * @access private
   */
  private static function generateElasticSearchQuery($parameters)
  {
    if (!is_array($parameters)) {
      $parameters = array($parameters);
    }
    
    $query = '*';
    
    $filters = array();
    
    foreach ($parameters as $key => $parameter) {
      if (is_int($key) && $parameter) {
        $query = str_replace('/', '\/', $parameter);
      } else {
        switch ($key) {
          case 'manufacturer': {
            if ($parameter instanceof \Models\Manufacturers) {
              $filters['manufacturerID'] = (int) $parameter->getManufacturerID();
            }
            
            break;
          }
          
          case 'section': {
            if ($parameter instanceof \Models\Sections) {
              $filters['sectionID'] = (int) $parameter->getSectionID();
            }
            
            break;
          }
          
          case 'category': {
            if ($parameter instanceof \Models\Categories) {
              $filters['categoryID'] = (int) $parameter->getCategoryID();
            }
            
            break;
          }
          
          case 'subCategory': {
            if ($parameter instanceof \Models\SubCategories) {
              $filters['subCategoryID'] = (int) $parameter->getSubCategoryID();
            }
            
            break;
          }
          
          case 'systemManfacturer': {
            if ($parameter instanceof \Models\SystemManfacturers) {
              $filters['partFinder.systemManufacturerID'] = (int) $parameter->getSystemManufacturerID();
            }
            
            break;
          }
          
          case 'modelFamily': {
            if ($parameter instanceof \Models\ModelFamilies) {
              $filters['partFinder.modelFamilyID'] = (int) $parameter->getModelFamilyID();
            }
            
            break;
          }
          
          case 'modelNumber': {
            if ($parameter instanceof \Models\ModelNumbers) {
              $filters['partFinder.modelNumberID'] = (int) $parameter->getModelNumberID();
            }
            
            break;
          }
          
          case 'distributor': {
            if ($parameter instanceof \Models\Distributors) {
              $filters['stockParts.distributorID'] = (int) $parameter->getDistributorID();
            }
            
            break;
          }
          
        }
      }
    }
    
    $body = array(
      'query' => array(
        'query_string' => array(
          'query'            => $query,
          'default_operator' => 'and'
        )
      )
    );
    
    $body['sort'] = array();
    
    if (isset($parameters['sort']) && $parameters['sort']) {
      foreach ($parameters['sort'] as $field => $order) {
        $body['sort'][$field] = array(
          'order' => $order
        );
      }
    } else {
      $body['sort']['_score'] = array(
        'order' => 'DESC'
      );
    }
    
    if (count($filters) > 0 || (isset($parameters['onlyStock']) && $parameters['onlyStock'])) {
      $body['filter'] = array(
        'and' => array()
      );
    }
    
    if (count($filters) > 0) {
      foreach ($filters as $key => $parameter) {
        $body['filter']['and'][] = array(
          'bool' => array(
            'must' => array(
              'term' => array(
                $key => $parameter
              )
            )
          )
        );
      }
    }
    
    if (isset($parameters['onlyStock']) && $parameters['onlyStock']) {
      $body['filter']['and'][] = array(
        'range' => array(
          'quantity' => array(
            'from' => 1
          )
        )
      );
      
      $body['filter']['and'][] = array(
        'range' => array(
          'unitPrice' => array(
            'from' => 1
          )
        )
      );
    }
    
    return $body;
  }
  
  public static function search(
    $parameters, 
    $pageNumber = 1,
    $itemsPerPage = 20)
  {
    //TODO Log search.
    
    $body = \Models\Parts::generateElasticSearchQuery($parameters);
    
    $startIndex = (($pageNumber - 1) * $itemsPerPage);
    
    $body['from'] = $startIndex;
    $body['size'] = $itemsPerPage;
    
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $params = array(
      'index' => 'partsit',
      'type'  => 'parts',
      'body'  => $body
      
    );
    $params['client'] = array();
    $params['client']['ignore'] = array(400, 404);
 
    $results = $elasticSearch->search(
      $params
    );
    
    $partsFromSearch = $results['hits']['hits'];
    
    $parts = array();
    
    foreach ($partsFromSearch as $part) {
      $part = \Models\Parts::findFirst(
        array(
          'partID = :partID:',
          'bind' => array(
            'partID' => $part['_id']
          ),
          //TODO Cache as partURL or ID?
          'cache' => array(
            'key'      => 'parts:id:' . $part['_id'],
            'lifetime' => 86400 // 1 day
          )
        )
      );
      
      if (!$part) {
        \Phalcon\DI::getDefault()->get('queue')->put(
          array(
            'task'   => 'mail',
            'action' => 'elasticSearchError',
            'params' => array(
              'parameters'   => $parameters,
              'pageNumber'   => $pageNumber,
              'itemsPerPage' => $itemsPerPage
            )
          )
        );
        
        continue;
      }
      
      $parts[] = $part;
    }
 
    //sort the part results as some of the results may not have a stock part index entry.
    //this will be a double check.
    uasort($parts, function($a, $b){
      $as = $a->getBestPrice(array('format' => false));
      $bs = $b->getBestPrice(array('format' => false));
      $as = (!$as) ? 0 : $as;
      $bs = (!$bs) ? 0 : $bs;
 
      if($as == $bs) {
        return 0;
      }
 
      return ($as > $bs) ? -1 : 1;
 
    });
    
    return array(
      'query'        => ((isset($parameters[0])) ? $parameters[0] : '*'),
      'pageNumber'   => $pageNumber,
      'itemsPerPage' => $itemsPerPage,
      'totalResults' => $results['hits']['total'],
      'parts'        => $parts
    );
  }
  
  
  public static function searchDistinct($field, $parameters, $limit = 20)
  {
    $body = \Models\Parts::generateElasticSearchQuery($parameters);
    
    
    
    $body['facets'] = array(
      'tag' => array(
        'terms' => array(
          'field' => $field,
          'size'  => $limit
        )
      )
    );
    
    
    
    $ignoreArrays = array(
      'partFinder' => array(
        'systemManufacturerID' => 'partFinder.systemManufacturerID',
        'modelFamilyID'        => 'partFinder.modelFamilyID',
        'modelNumberID'        => 'partFinder.modelNumberID'
      ),
      
      'categories' => array(
        'sectionID'            => 'sectionID',
        'categoryID'           => 'categoryID',
        'subCategoryID'        => 'subCategoryID'
      )
    );
    
    
    if (isset($body['filter']['and'])) {
      foreach ($ignoreArrays as $ignoreArray) {
        if (!array_key_exists($field, $ignoreArray)) {
          continue;
        }
        
        foreach ($ignoreArray as $ignore) {
          foreach ($body['filter']['and'] as $i => $filter) {
            if (array_key_exists($ignore, $filter['bool']['must']['term'])) {
              unset($body['filter']['and'][$i]);
            }
          }
        }
      }
      
      if (count($body['filter']['and']) == 0) {
        unset($body['filter']['and']);
      }
      
      if (count($body['filter']) == 0) {
        unset($body['filter']);
      }
    }
    
    $body['facets']['tag']['facet_filter'] = $body['filter'];
    
    
    
    
    
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $results = $elasticSearch->search(
      array(
        'index' => 'partsit',
        'type'  => 'parts',
        'body'  => $body,
        'client' => array(
          'ignore' => array(
            404, 400
          )
        )
      )
    );
    
    $results = $results['facets']['tag']['terms'];
    
    foreach ($results as $i => $result) {
      $results[$i] = $result['term'];
    }
    
    return $results;
  }
  
  public static function searchTechSpecs($parameters)
  {
    $body = \Models\Parts::generateElasticSearchQuery($parameters);
    
    unset($body['sort']);
    
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $results = $elasticSearch->search(
      array(
        'index' => 'partsit',
        'type'  => 'parts',
        'body'  => $body
      )
    );
    
    $techSpecs = array();
    
    $parts = $results['hits']['hits'];
    
    foreach ($parts as $part) {
      if (!isset($part['_source']['partTechSpecs'])) {
        continue;
      }
      
      foreach ($part['_source']['partTechSpecs'] as $partTechSpec) {
        $name  = $partTechSpec['name'];
        $value = $partTechSpec['value'];
        
        if (!array_key_exists($name, $techSpecs)) {
          $techSpecs[$name] = array();
        }
        
        if (!in_array($value, $techSpecs[$name])) {
          $techSpecs[$name][] = $value;
        }
      }
    }
    
    return $techSpecs;
  }
  
  public static function searchJSON($query)
  {
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $results = $elasticSearch->search(
      array(
        'index' => 'partsit',
        'type'  => 'parts',
        'body'  => array(
          'query' => array(
            'query_string' => array(
              'query' => $query
            )
          ),
          'facets' => array(
            'tag' => array(
              'terms' => array(
                'field' => 'tag',
                'size'  => 10,
                'order' => 'term'
              )
            )
          ),
          'from' => 0,
          'size' => 10
        ),
        'ignore' => array(
          404, 400
        )
      )
    );
    
    $parts = $results['hits']['hits'];
    
    foreach ($parts as $i => $part) {
      $parts[$i] = array(
        'description' => $part['_source']['shortDescription'],
        'value'       => $part['_source']['manufacturer'] . ' ' . $part['_source']['partNumber'],
        'partURL'     => $part['_source']['partURL']
      );
    }
    
    return json_encode($parts);
  }
  
  public static function searchAutocomplete($query)
  {
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $results = $elasticSearch->search(
      array(
        'index' => 'partsit',
        'type'  => 'parts',
        'body'  => array(
          'query' => array(
            'query_string' => array(
              'query' => $query,
              'default_operator' => 'and'
            )
          ),
          'from' => 0,
          'size' => 10
        ),
        'client' => array(
          'ignore' => array(
            404, 400
          )
        )
      )
    );
    
    $parts = $results['hits']['hits'];
    
    foreach ($parts as $i => $part) {
      $de = $part['_source']['partNumber'] . ' ' . $part['_source']['shortDescription'];
      
      $desc = (strlen($de) > 35)
          ? substr($de, 0, 35) . '...'
          : $de;
      
      $parts[$i] = array(
        'label' => $desc,
        'value' => '/product/' . $part['_source']['partURL']
      );
    }
    
    if ($results['hits']['total'] > 10) {
      $parts[] = array(
        'label' => 'more &gt; &gt;',
        'value' => '/search?q=' . $query
      );
    }
    
    return json_encode($parts);
    
  }
  
  public function getPartID()
  {
    return $this->partID;
  }
  
  public function getPartNumber()
  {
    return $this->partNumber;
  }
  
  public function getManufacturerID()
  {
    return $this->manufacturerID;
  }
  
  public function getTitle()
  {
    return ($this->title ?: $this->getPartNumber());
  }
  
  public function getPartURL()
  {
    return $this->partURL;
  }
  
  public function getShortDescription($force = false)
  {
 
    if($force) {
      return $this->shortDescription;
    }  
 
    if(isset($this->title)) {
      $t = $this->title;
      $m = $this->getManufacturers();
      $m = ($m) ? $m->getManufacturer() : '';
 
      if(strpos(strtolower($t), strtolower($m)) === false) {
        return $m . $t;
      }
 
      return $t;
    }
 
    return $this->shortDescription;
  }
  
  public function getSubCategoryID()
  {
    return $this->subCategoryID;
  }
  
  public function getDateCreated()
  {
    return $this->dateCreated;
  }
  
  public function getDateModified()
  {
    return $this->dateModified;
  }
  
  public function getAvailableQuantity()
  {
    $availableQuantity = \Models\StockParts::sum(
      array(
        'partID = :partID:',
        'bind' => array(
          'partID' => $this->getPartID()
        ),
        'column' => 'quantity'
      )
    );
    
    return $availableQuantity;
  }
 
  public function getRefurbishedPrice($update = false, $format = false) {
    //if update, this function forcefully updates ebay, brokerbin & alibaba.
    if($update) {
      $console = \Phalcon\DI::getDefault()->get('console');
      //ebay.
      $console->handle(
        array(
          'task' => 'ebay',
          'action' => 'importIndividual',
          'params' => array(
            0 => $this->getPartID()
          )
        )
      );
      //brokerbin.
      $console->handle(
        array(
          'task' => 'brokerbin-parts',
          'action' => 'importIndividualForce',
          'params' => array(
            0 => $this->getPartID()
          )
        )
      );
    }
 
    $ebay = \Models\Distributors::findFromURL('ebay');
    $bb = \Models\Distributors::findFromURL('brokerbin');
    $ali = \Models\Distributors::findFromURL('alibaba');
 
    //see if there is a price from these three sources and return the cheapest.
    $stockPart = \Models\StockParts::findFirst(
      array(
        'partID = :partID: AND distributorID IN (:ebay:,:bb:,:ali:)', 
        'order' => 'unitPrice ASC', 
        'bind' => array(
          'ebay' => $ebay->getDistributorID(), 
          'bb' => $bb->getDistributorID(), 
          'ali' => $ali->getDistributorID(), 
          'partID' => $this->getPartID()
        )
      )
    );
 
    if(!$stockPart) {
      return false;
    }
 
    //times by 1.15.
    $price = round((floatval($stockPart->getUnitPrice()) * 1.15), 2);
 
    if($format) {
      return number_format($price, 2);
    }
 
    return $price;
 
  }
  
  public function getBestStockPart($parameters = array())
  {
    $defaultParameters = array(
      'minLeadTime' => 0,
      'maxLeadTime' => 7
    );
 
    $distro = \Models\Distributors::findFromURL('charterpoint');
 
    $stockPart = \Models\StockParts::findFirst(array(
      'partID = :partID: AND distributorID = :disID:',
      'bind' => array(
        'partID' => $this->getPartID(),
        'disID' => $distro->getDistributorID()
      )
    ));
 
    if(!$stockPart) {
      
      $parameters = is_array($parameters) ? array_merge($defaultParameters, $parameters)
                        : $defaultParameters;
 
      $ebay = \Models\Distributors::findFromURL('ebay');
      $bb = \Models\Distributors::findFromURL('brokerbin');
      $ali = \Models\Distributors::findFromURL('alibaba');
      
      /* distributorID NOT IN (:ebay:,:bb:,:ali:) */
 
      $stockPart = \Models\StockParts::findFirst(
        array(
          'partID = :partID:
          AND
          (estimatedLeadTime BETWEEN :minLeadTime: AND :maxLeadTime:)
          AND
          quantity > 0
          AND
          distributorID IN (1,26,23,28,25,63,12,29,74,21,68,75,76,77,64,11,78)
          ',
          'bind' => array(
            'partID'      => $this->getPartID(),
            'minLeadTime' => $parameters['minLeadTime'],
            'maxLeadTime' => $parameters['maxLeadTime'],
            /**'ebay' => $ebay->getDistributorID(),
            'bb' => $bb->getDistributorID(),
            'ali' => $ali->getDistributorID()**/
          ),
          'order' => array(
            'unitPrice'
          )
        )
      );
 
      if(!$stockPart) {
        $stockPart = \Models\StockParts::findFirst(
          array(
            'partID = :partID:
            AND
            (estimatedLeadTime BETWEEN :minLeadTime: AND :maxLeadTime:)
            AND
            distributorID IN (1,26,23,28,25,63,12,29,74,21,68,75,76,77,64,11,78)
            ',
            'bind' => array(
              'partID'      => $this->getPartID(),
              'minLeadTime' => $parameters['minLeadTime'],
              'maxLeadTime' => $parameters['maxLeadTime'],
              /**'ebay' => $ebay->getDistributorID(),
              'bb' => $bb->getDistributorID(),
              'ali' => $ali->getDistributorID()**/
            ),
            'order' => array(
              'unitPrice'
            )
          )
        );
      }  
    }
    
    return $stockPart;
  }
 
  /**
   * determines if this part has a valid fixed price.
   * @return \Models\FixedPrice|bool returns the fixed price on true, false otherwise.
   * @access public 
   */
  public function hasFixedPrice() 
  {
    $now = new \DateTime();
    $now = $now->format('Y-m-d');
    $fixedPrice = \Models\FixedPrice::findFirst(array(
      'partID = ?1 AND validTo > ?2',
      'bind' => array(
        1 => $this->getPartID(),
        2 => $now
      )
    ));
 
    return ($fixedPrice instanceof \Models\FixedPrice) 
      ? $fixedPrice : false;
  }
 
  /**
   * A new pricing function which utilizes a new markup schema.
   * valid parameters are: 
   *     single : true|false - whether to just return the price for one.
   *     quantity : int - whether to get the price for a specific quantity
   *     vat : true|false - whether to calculate vat for the prices.
   *     format :  true|false - whether to format the price.
   *
   */
  public function getBestPrice($parameters = array()) 
  {
    
    $now = new \DateTime();
    $now = $now->format('Y-m-d');
    $salePrice = 0; $quantity = 1;
    $single = (isset($parameters['single'])) ? $parameters['single'] : true;
    $invalidBrokers = \Models\Metadata::get('pricing', 'invalidBrokers');
    $invalidBrokers = json_decode($invalidBrokers->getValue(), true);
    //make sure they every element is an int.
    array_walk($invalidBrokers, function(&$value){
      $value = intval($value);
    });
    $isFromInvalid = false;
    $hasVat = (isset($parameters['vat']) && is_bool($parameters['vat'])) ? $parameters['vat'] : false;
 
    //fixed prices now are cost prices, so then the matrix can work out
    //quantity based prices based on the formula.
    $fixedPrice = \Models\FixedPrice::findFirst(array(
      'partID = ?1 AND validTo > ?2',
      'bind' => array(
        1 => $this->getPartID(),
        2 => $now
      )
    ));
 
    $hasCustomMarkup = false;
 
    if(!$fixedPrice) {
      //if this product is on promotion, always use the
      //distributor which was used.
      $promo = $this->getPromotion();
 
      if($promo instanceof \Models\PromoParts) { //if its any promotion type.
        $stockPart = $promo->getStockParts();
        
      }
 
      if(!isset($stockPart) || !$stockPart) {
        $stockPart = $this->getBestStockPart($parameters);
      }
 
      if (!$stockPart) {
        return false;
      }
 
      $isFromInvalid = (in_array($stockPart->getDistributorID(), $invalidBrokers));
        
      $salePrice = $stockPart->getUnitPrice();
      $quantity = ($stockPart->getQuantity() < 2) ? 10 : $stockPart->getQuantity(); 
 
      $hasCustomMarkup = (\Models\CustomDistributorMarkup::count(
        array(
          'distributorID = ?1',
          'bind' => array(
            1 => $stockPart->getDistributorID()
          )
        )
      ) > 0);  
      
    } else {
      $salePrice = $fixedPrice->getPrice();
      $quantity = $fixedPrice->getQuantity();
      if(!isset($quantity)) {
        $quantity = 10; //default a fixed price quantity to 10.
      }
 
      $hasCustomMarkup = $fixedPrice->hasCustomMarkup();
    }
 
    $salePrice = floatval($salePrice); $quantity = intval($quantity);
 
    if($salePrice == 0) {
      $hasRequired = ($fixedPrice instanceof \Models\FixedPrice && $fixedPrice->getRequiredSalePrice() > 0);
      if(!$hasRequired) {
        return false;
      }
    }
 
    $quantitiesShown = \Models\Metadata::get('pricing', 'quantitiesShown');
    $quantitiesShown = json_decode($quantitiesShown->getValue(), true);
    //make sure they every element is an int.
    array_walk($quantitiesShown, function(&$value){
      $value = intval($value);
    });
 
    //work out quantity based prices based on the markup.
    $prices = array();
    $quantitiesShown = (isset($parameters['quantity']) && is_numeric($parameters['quantity']))
         ? array($parameters['quantity']) 
         : $quantitiesShown;
    $single = (isset($parameters['quantity']) && is_numeric($parameters['quantity'])) ? true : $single;
    $markup = \Models\Metadata::findFirst('category = "pricing" and name = "markupRange"');
    $markup = json_decode($markup->getValue(), true);
 
    //determine if the price was from an un-reliable source.
    $quantitiesShown = (!$isFromInvalid) ? $quantitiesShown : array(1);
 
    $del = \Models\Metadata::get('pricing', 'deliveryCost');
    $del = floatval($del->getValue());
    $mov = \Models\Metadata::get('pricing', 'minimumOrderValue');
    $mov = floatval($mov->getValue());
    $minusvat = \Models\Metadata::get('pricing', 'minus-vat');
    $minusvat = floatval($minusvat->getValue());
    $vat = \Models\Metadata::get('pricing', 'vat');
    $vat = floatval($vat->getValue());
 
 
 
    foreach($quantitiesShown as $q) {
      if($q <= $quantity) { //if we have enough data to show this price.
        //determine if we are a fixed price and if we have a required sale price.
        if($fixedPrice instanceof \Models\FixedPrice) {
          $rsp = $fixedPrice->getRequiredSalePrice();
          if(isset($rsp) && $rsp > 0 && $rsp > $fixedPrice->getPrice()) {
            //generate the sale price by reverse engineering the rsp.
            $rsp -= $del;
            foreach ($markup as $entry) {
              if($rsp >= floatval($entry['min']) && ($rsp <= floatval($entry['max']) || intval($entry['max'] == -1))) {
                //if the price is within the range, apply the markup.
                switch($entry['type']) {
                  case '+':
                    $rsp -= (floatval($entry['amount']));
                    break;
                  case '*':
                    $rsp /= (floatval($entry['amount']));
                    break;
                }
                break;
              }
            }
 
            $salePrice = $rsp;
          }
        }
 
        //die(var_dump($salePrice));
 
        $up = $salePrice * $q;
        if($fixedPrice instanceof \Models\FixedPrice && $hasCustomMarkup) {
          $up = $fixedPrice->generatePrice($up);
        } elseif(isset($stockPart) && $hasCustomMarkup) {
 
          $c = \Models\CustomDistributorMarkup::find(
            array(
              'distributorID = ?1',
              'bind' => array(
                1 => $stockPart->getDistributorID()
              ),
              'order' => 'distributorID, min'
            )
          );
 
          foreach ($c as $cu) {
            if($up >= floatval($cu->getMin()) && ($up <= floatval($cu->getMax()) || intval($cu->getMax()) == -1)) {
              $custom = $cu;
            }
          }
          if ($custom instanceof \Models\CustomDistributorMarkup) {
            $up = $custom->generatePrice($up);
          } else {
            //if there is no valid range for this markup use default price structure.
            $up = $this->applyMarkup($markup, $up);
          }
        } else {
          $up = $this->applyMarkup($markup, $up);
        }
 
        //add delivery.
        $up += $del;
 
 
        //determine if the price is below the minimum order value.
        if($up < $mov) {
          $up = $mov; //set the minimum order value.
        }
        
        if($hasVat) {
          //add vat.
          $up *= $vat;
        }
 
        //apply options.
        // //ex-vat.
        // if(isset($parameters['vat']) && !$parameters['vat']) {
        //   $up *= $minusvat;
        // }
        //whether to number_format the price.
        if(isset($parameters['format']) && $parameters['format']) {
          $up = number_format($up, 2, '.', '');
        }
 
        //as the basket works in the unit price, we need to make sure
        //any rounding error is dealt with at this point in order to
        //stop the risk of the prices being inaccurate when its transfered to the
        //basket.
        $s = round($up / $q, 2);
        $up = $s * $q;
        $prices[$q] = $up;
      }
    }
 
    if($single) {
      //return the price for a single part.
      $prices = array_values($prices);
      return $prices[0];
    }
 
    return $prices;
 
  }
 
  private function applyMarkup($markup, $up) 
  {
    foreach ($markup as $entry) {
      if($up >= floatval($entry['min']) && ($up <= floatval($entry['max']) || intval($entry['max'] == -1))) {
        //if the price is within the range, apply the markup.
        switch($entry['type']) {
          case '+':
            $up += (floatval($entry['amount']));
            break;
          case '*':
            $up *= (floatval($entry['amount']));
            break;
        }
        break;
      }
    }
    return $up;
  }
  
  public function isInStock()
  {
    return ($this->countStockParts() > 0);
  }
  
  public function hasPartImage()
  {
    $manufacturerURL = $this->getManufacturers()->getManufacturerURL();
    
    $imageFolder = '/img/products/' . $manufacturerURL . '/' . $this->getPartURL();
    
    $images = is_dir('../public' . $imageFolder)
      ? scandir('../public' . $imageFolder)
      : array();
    
    sort($images);
    
    foreach ($images as $image) {
      if ($image == '.' || $image == '..') {
        continue;
      }
      
      return true;
    }
    
    return false;
  }
  
  
  public function getPartImage()
  {
 
    //determine if this product has any internet images.
    $images = $this->getImages();
 
    if(count($images) > 0) {
      $i = $images[0];
      return '/resources/image/' . $i->getHash();
    }
 
    $manufacturerURL = $this->getManufacturers()->getManufacturerURL();
    
    $imageFolder = 'img/products/' . $manufacturerURL . '/' . $this->getPartURL();
    
    $images = is_dir('../public/' . $imageFolder)
      ? scandir('../public/' . $imageFolder)
      : array();
    
    sort($images);
    
    foreach ($images as $image) {
      if ($image == '.' || $image == '..') {
        continue;
      }
 
      $extension = pathinfo('../public/'.$imageFolder.'/'.$image, PATHINFO_EXTENSION);
      if(!isset($extension) || !in_array($extension, array('jpg', 'jpeg', 'png', 'gif', 'svg'))) {
        continue;
      }
      
      return rtrim($this->getDI()->get('url')->getBaseUri(), '/') . '/' . $imageFolder . '/' . $image;
    }
    
    $manufacturerImage = $this->getManufacturers()->getManufacturerImage();
    
    return ($manufacturerImage)
        ? $manufacturerImage
        : rtrim($this->getDI()->get('url')->getBaseUri(), '/') . '/img/placeholder.png';
  }
 
  public static function countPartsWithImages() {
    return PartImages::count(array(
      'action = "approve"'
    ));
  }
  
  public function getSimilarProducts($number = 4)
  {
    $query = $this->getManufacturers()->getManufacturer() . ' ' . $this->getPartNumber() . ' ' . $this->getShortDescription();
    
    if ($this->getTitle() != $this->getPartNumber()) {
      $query .= ' ' . $this->getTitle();
    }
    
    $query = preg_replace('/[^a-z0-9\-\._]/i', ' ', $query);
    
    $body = \Models\Parts::generateElasticSearchQuery(
      array(
        $query
      )
    );
    
    $body['facets'] = array(
      'tag' => array(
        'terms' => array(
          'field' => 'tag',
          'size'  => $number,
          'order' => 'term'
        )
      )
    );
    
    /*
     * We need to remove the current part from the search. We're
     * assuming it'll be one of the first.
     */
    $body['size'] = ($number + 1);
    
    $elasticSearch = \Phalcon\DI::getDefault()->get('elasticSearch');
    
    $results = $elasticSearch->search(
      array(
        'index' => 'partsit',
        'type'  => 'parts',
        'body'  => $body,
        'client' => array(
          'ignore' => array(404, 400)
        )
      )
    );
    
    $parts = $results['hits']['hits'];
    
    foreach ($parts as $i => $part) {
      if ($part['_source']['partURL'] == $this->getPartURL()) {
        unset($parts[$i]);
        
        continue;
      }
      
      $parts[$i] = \Models\Parts::findFirst(
        array(
          'partID = :partID:',
          'bind' => array(
            'partID' => $part['_id']
          ),
          'cache' => array(
            'key'      => 'parts:id:' . $part['_id'],
            'lifetime' => 86400 // 1 day
          )
        )
      );
    }
    
    // Just in case this part wasn't in the search results.
    $parts = array_splice($parts, 0, $number);
    
    return $parts;
  }
  
  public function getOverallRating()
  {
    $overallRating = \Models\Reviews::average(
      array(
        'column' => 'starRating',
        'partID = :partID:
        AND
        isApproved = 1',
        'bind' => array(
          'partID' => $this->getPartID()
        )
      )
    );
    
    return round($overallRating, 2);
  }
  
  
  
  public function setPartNumber($partNumber)
  {
    $this->partNumber = $partNumber;
  }
  
  public function setManufacturerID($manufacturerID)
  {
    $this->manufacturerID = $manufacturerID;
  }
  
  public function setTitle($title)
  {
    $this->title = $title;
  }
  
  public function setPartURL($partURL)
  {
    $this->partURL = $partURL;
  }
  
  public function setShortDescription($shortDescription)
  {
    $filter = $this->getDI()->get('filter');
    
    $shortDescription = $filter->sanitize($shortDescription, 'description');
    
    $this->shortDescription = $shortDescription;
  }
  
  public function setSubCategoryID($subCategoryID)
  {
    $this->subCategoryID = $subCategoryID;
  }
  
  
  
  public function asArray()
  {
    $averagePrice = \Models\StockParts::average(
      array(
        'partID = :partID:',
        'bind' => array(
          'partID' => $this->getPartID()
        ),
        'column' => 'unitPrice'
      )
    );
    
    $quantity = \Models\StockParts::sum(
      array(
        'partID = :partID:',
        'bind' => array(
          'partID' => $this->getPartID()
        ),
        'column' => 'quantity'
      )
    );
    
    return array(
      'manufacturer'     => $this->getManufacturers()->getManufacturer(),
      'partNumber'       => $this->getPartNumber(),
      'shortDescription' => $this->getShortDescription(),
      
      'averagePrice'     => number_format($averagePrice, 2, '.', ''),
      'quantity'         => number_format($quantity,     0, '.', ''),
      
      'url'              => 'https://www.partsit.com/product/' . $this->getPartURL()
    );
  }
  
  
  
  public function changeManufacturer(\Models\Manufacturers $newManufacturer)
  {
    //TODO Change part image location, if there is one.
    
    $oldManufacturer    = $this->getManufacturers();
    $oldManufacturerURL = $oldManufacturer->getManufacturerURL();
    $oldPartURL         = $this->getPartURL();
    $newManufacturerURL = $newManufacturer->getManufacturerURL();
    
    if (substr($oldPartURL, 0, strlen($oldManufacturerURL)) != $oldManufacturerURL) {
      throw new \Exception('Old Part URL is bad: <strong>' . $oldPartURL . '</strong>');
    }
    
    $newPartURL = $newManufacturerURL . substr($oldPartURL, strlen($oldManufacturerURL));
    
    $tempPart = \Models\Parts::findFirst(
      array(
        'partURL = :partURL:',
        'bind' => array(
          'partURL' => $newPartURL
        )
      )
    );
    
    if ($tempPart) {
      throw new \Exception('New Part URL already exists: <strong>' . $newPartURL . '</strong>');
    }
    
    $this->setPartURL(
      $newPartURL
    );
    
    $this->setManufacturerID(
      $newManufacturer->getManufacturerID()
    );
    
    $this->save();
    
    return $this;
  }
  
  
  
  
  
  
  
  //TODO
  public function getSapParts()
  {
    $sapPart = \Models\SAP\Parts::findFirst(
      array(
        'manufacturer = :manufacturer:
        AND
        partNumber = :partNumber:',
        'bind' => array(
          'manufacturer' => $this->getManufacturers()->getManufacturer(),
          'partNumber'   => $this->getPartNumber()
        )
      )
    );
    
    return $sapPart;
  }
  
  
  
  public function getSubCategories($parameters = array())
  {
    if (isset($parameters[0])) {
      $parameters[0] = '(' . $parameters[0] . ') AND subCategoryID = :subCategoryID:';
    } else {
      $parameters[0] = 'subCategoryID = :subCategoryID:';
    }
    
    if (!isset($parameters['bind'])) {
      $parameters['bind'] = array();
    }
    
    $parameters['bind']['subCategoryID'] = $this->getSubCategoryID();
    
    $parameters['cache'] = array(
      'key'      => 'subCategories:id:' . $this->getSubCategoryID(),
      'lifetime' => 604800 // 1 week
    );
    
    $subCategory = \Models\SubCategories::findFirst(
      $parameters
    );
    
    return $subCategory;
  }
  
  
  
  
  public function getManufacturers($parameters = array())
  {
    if (isset($parameters[0])) {
      $parameters[0] = '(' . $parameters[0] . ') AND manufacturerID = :manufacturerID:';
    } else {
      $parameters[0] = 'manufacturerID = :manufacturerID:';
    }
    
    if (!isset($parameters['bind'])) {
      $parameters['bind'] = array();
    }
    
    $parameters['bind']['manufacturerID'] = $this->getManufacturerID();
    
    $parameters['cache'] = array(
      'key'      => 'manufacturers:id:' . $this->getManufacturerID(),
      'lifetime' => 604800 // 1 week
    );
    
    $manufacturer = \Models\Manufacturers::findFirst(
      $parameters
    );
    
    return $manufacturer;
  }
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  public function getAlternateParts()
  {
    $alternateParts1 = \Models\AlternateParts::find(
      array(
        'partID = :partID:',
        'bind' => array(
          'partID' => $this->getPartID()
        )
      )
    );
    
    $alternateParts2 = \Models\AlternateParts::find(
      array(
        'alternativePartID = :alternativePartID:',
        'bind' => array(
          'alternativePartID' => $this->getPartID()
        )
      )
    );
    
    $alternateParts = array();
    
    foreach ($alternateParts1 as $alternatePart1) {
      $alternatePart = $alternatePart1->getAlternativeParts();
      
      $alternateParts[] = $alternatePart;
    }
    
    foreach ($alternateParts2 as $alternatePart2) {
      $alternatePart = $alternatePart2->getParts();
      
      if (in_array($alternatePart, $alternateParts)) {
        continue;
      }
      
      $alternateParts[] = $alternatePart;
    }
    
    return $alternateParts;
  }
 
  public function getFeaturesByGroup($columns = 3) {
 
    $config = new \Phalcon\Config\Adapter\Ini(
            'config/' . ENVIRONMENT . '.ini'
        );
 
    $connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
            'host' => $config->dbPartsIT->hostname,
            'username' => $config->dbPartsIT->username,
            'password' => $config->dbPartsIT->password,
            'dbname' => $config->dbPartsIT->database
        ));
 
        $connection->connect();
 
        $query = 'SELECT 
              feature_group.name as name, 
              featureID as id
            FROM partsit.part_features 
 
            INNER JOIN feature_group USING (groupId)
 
            WHERE part_features.partID = ? 
 
            ORDER BY feature_group.name ASC';
 
        $result = $connection->query($query, array($this->getPartID()));
 
        $result->setFetchMode(\Phalcon\Db::FETCH_ASSOC);
        $results = $result->fetchAll($result);
 
        //return an array of array('feature_name' => array(feature_models));
        $data = array();
 
        foreach($results as $entry) {
 
          if(!array_key_exists($entry['name'], $data)) {
            $data[$entry['name']] = array();
          }
 
          $data[$entry['name']][] = \Models\PartFeatures::findFirst(array('featureID = ?1', 'bind' => array(1 => $entry['id'])));
 
        }
 
        $data = array_chunk($data, ceil(count($data) / $columns), true);
 
        return $data;
 
  }
 
}
#10Models\Parts::searchTechSpecs(Array([0] => null, [onlyStock] => , [section] => Object(Models\Sections), [category] => Object(Models\Categories), [subCategory] => Object(Models\SubCategories), [manufacturer] => , [modelNumber] => , [partTechSpecs] => Array()))
/home/ubuntu/partsit-site/app/controllers/CategoryController.php (777)
<?php
 
namespace Controllers;
 
class CategoryController extends \Controllers\ControllerBase
{
  public function indexAction()
  {
    return $this->response->redirect(
      'categories'
    );
  }
  
  
  
  public function sectionAction()
  {
    $selectedSectionURL = $this->dispatcher->getParam('sectionURL', 'urlSlug');
    
    $pageNumber = $this->request->getQuery('page') ?: 1;
    
    $query = $this->request->getQuery('q', 'trim');
    
    $selectedManufacturerURL = $this->request->getQuery('manufacturer', 'urlSlug');
    $selectedModelNumberID   = $this->request->getQuery('modelNumber',  'int');
    
    
    
    $selectedTechSpecs = $this->request->getQuery('techSpecs');
    
    
    
    $showOnlyStock = !!$this->request->getQuery('onlyStock');
    
    
    
    
    $itemsPerPage = $this->request->getQuery('itemsPerPage', 'int', 10);
    
    
    
    // Force a hard maximum of 100 for the items per page.
    $itemsPerPage = min($itemsPerPage, 100);
    
    
    
    
    
    
    $selectedSection = \Models\Sections::findFromURL(
      $selectedSectionURL
    );
    
    if (!$selectedSection) {
      return $this->error404();
    }
    
    
    
    
    
    $selectedManufacturer = ($selectedManufacturerURL)
        ? \Models\Manufacturers::findFromURL($selectedManufacturerURL)
        : false;
    
    $selectedModelNumber = ($selectedModelNumberID)
        ? \Models\ModelNumbers::findFirstByModelNumberID($selectedModelNumberID)
        : false;
    
    
    
    
    
    $searchParameters = array(
      $query,
      
      'onlyStock'     => $showOnlyStock,
      
      'section'       => $selectedSection,
      
      'manufacturer'  => $selectedManufacturer,
      'modelNumber'   => $selectedModelNumber,
      
      'partTechSpecs' => array(),
      
      'sort'          => array(
        'manufacturer' => 'ASC',
        'partNumber'   => 'ASC'
      )
    );
 
        $nonMan = array(
            $query,
 
            'onlyStock'     => $showOnlyStock,
 
            'section'       => $selectedSection,
 
            'modelNumber'   => $selectedModelNumber,
 
            'partTechSpecs' => array(),
 
            'sort'          => array(
                'manufacturer' => 'ASC',
                'partNumber'   => 'ASC'
            )
        );
    
    
    
    if ($selectedTechSpecs) {
      foreach ($selectedTechSpecs as $selectedTechSpecName => $selectedTechSpecValue) {
        if (!$selectedTechSpecName) {
          continue;
        }
        
        $selectedTechSpecName = preg_replace('/[^a-z0-9]+/i', '', $selectedTechSpecName);
        
        $searchParameters['partTechSpecs'][$selectedTechSpecName] = $selectedTechSpecValue;
      }
    }
    
    
    
    
    
    $results = \Models\Parts::search(
      $searchParameters,
      $pageNumber,
      $itemsPerPage
    );
    
    
    
    $parts        = $results['parts'];
    $totalResults = $results['totalResults'];
    
    
    
    
    
    $url = '/category/' . $selectedSectionURL . '/';
    
    
    $url .= '?' . http_build_query(
      array(
        'q'            => $query,
        
        'onlyStock'    => $showOnlyStock,
        'manufacturer' => $selectedManufacturerURL,
        'modelNumber'  => $selectedModelNumberID,
        
        'itemsPerPage' => $itemsPerPage,
        
        'page'         => ''
      ),
      null,
      '&amp;'
    );
    
    
    $sections = \Models\Sections::find(
      array(
        'order' => 'section ASC'
      )
    );
    
    $categories = $selectedSection->getCategories(
      array(
        'order' => 'category ASC'
      )
    );
    
    
    $filterManufacturers = \Models\Parts::searchDistinct('manufacturerID', $nonMan, 1000);
    
    $filterSystemManufacturers = \Models\Parts::searchDistinct('systemManufacturerID',  $searchParameters, 1000);
    $filterModelFamilies       = \Models\Parts::searchDistinct('modelFamilyID',         $searchParameters, 1000);
    $filterModelNumbers        = \Models\Parts::searchDistinct('modelNumberID',         $searchParameters, 1000);
    
    
    
    $manufacturers = \Models\Manufacturers::find(
      array(
        'order' => 'manufacturer ASC'
      )
    );
    
    
    
    $techSpecs = \Models\Parts::searchTechSpecs($searchParameters);
    
    
    
    $this->view->sections   = $sections;
    $this->view->categories = $categories;
    $this->view->techSpecs  = $techSpecs;
    
    
    
    
    $this->view->filterManufacturers = $filterManufacturers;
    
    
    $this->view->filterSystemManufacturers = $filterSystemManufacturers;
    $this->view->filterModelFamilies       = $filterModelFamilies;
    $this->view->filterModelNumbers        = $filterModelNumbers;
    
    
    
    $this->view->manufacturers = $manufacturers;
    
    
    $this->view->parts = $parts;
    
    $this->view->selectedSection      = $selectedSection;
    $this->view->selectedManufacturer = $selectedManufacturer;
    $this->view->selectedTechSpecs    = $selectedTechSpecs;
    $this->view->selectedModelNumber  = $selectedModelNumber;
    
    $this->view->itemsPerPage = $itemsPerPage;
    
    $this->view->query = $query;
    
    $this->view->showOnlyStock = $showOnlyStock;
    
    $this->view->totalResults = $totalResults;
    
    
    
    $this->view->pagination = array(
      'pageNumber'   => $pageNumber,
      'itemsPerPage' => $itemsPerPage,
      'totalItems'   => $totalResults,
      'url'          => $url,
      'what'         => 'product'
    );
    
    
    
    $this->tag->prependTitle(
      $selectedSection->getSection() . ' parts | '
    );
    //Show Special offers on the side of search reults.
 
    
    $d = \Models\Metadata::findFirst('category = "promotional" and name = "distributors"');
    $distis = json_decode($d->getValue(), true);
 
    //get selected SECTION
    $getSec = $selectedSection->getSection();
 
    $cats = array();
    foreach($categories as $cat) {
      $cats[] = $cat->getCategoryURL();
    }
 
        $manus = array();
 
         $fs = array(
          'distributor_urls' => $distis,
      'category_urls' => $cats,
           'manufacturer_urls' => $manus
         );
    
        //get 4 random special offers per page, including a mail shot entry if we need to.
        $msi = mt_rand(0, 4);
        $template = new \Template();
        $template->setTemplateDir('offer-views');
 
        $offers = \Models\PromoParts::getSitePromotions($fs, 2, 1, false);
 
        //render the html for the selected offers.
        //format the params.
        $params = array();
        if(count($offers > 0)) {
            for($i = 0; $i < count($offers); $i++) {
                if($i == $msi) {
                    //enter this first at this index.
                    $params[] = array(
                        'type' => 'mailshot',
                        'object' => null
                    );
                }
                $params[] = array('type' => 'promo', 'object' => $offers[$i]);
            }
        }
 
        $html = $template->renderTemplate('offers', 'home', array('objects' => $params));
 
        $this->view->initialOffers = $html;
  }
  
  
  public function categoryAction()
  {
    $selectedSectionURL  = $this->dispatcher->getParam('sectionURL',  'urlSlug');
    $selectedCategoryURL = $this->dispatcher->getParam('categoryURL', 'urlSlug');
    
    $pageNumber = $this->request->getQuery('page') ?: 1;
    
    $query = $this->request->getQuery('q', 'trim');
    
    $selectedManufacturerURL = $this->request->getQuery('manufacturer', 'urlSlug');
    $selectedModelNumberID   = $this->request->getQuery('modelNumber',  'int');
    
    
    
    $selectedTechSpecs = $this->request->getQuery('techSpecs');
    
    
    
    $showOnlyStock = !!$this->request->getQuery('onlyStock');
    
    
    
    
    $itemsPerPage = $this->request->getQuery('itemsPerPage', 'int', 10);
    
    
    
    // Force a hard maximum of 100 for the items per page.
    $itemsPerPage = min($itemsPerPage, 100);
    
    
    
    
    
    
    $selectedCategory = \Models\Categories::findFromURL(
      $selectedSectionURL,
      $selectedCategoryURL
    );
    
    if (!$selectedCategory) {
      return $this->error404();
    }
    
    $selectedSection = $selectedCategory->getSections();
    
    
    
    
    
    $selectedManufacturer = ($selectedManufacturerURL)
        ? \Models\Manufacturers::findFromURL($selectedManufacturerURL)
        : false;
    
    $selectedModelNumber = ($selectedModelNumberID)
        ? \Models\ModelNumbers::findFirstByModelNumberID($selectedModelNumberID)
        : false;
    
    
    
    
    
    $searchParameters = array(
      $query,
 
      'onlyStock'     => $showOnlyStock,
 
      'category'      => $selectedCategory,
 
      'manufacturer'  => $selectedManufacturer,
      'modelNumber'   => $selectedModelNumber,
 
      'partTechSpecs' => array(),
 
      'sort'          => array(
        'manufacturer' => 'ASC',
        'partNumber'   => 'ASC'
      )
    );
 
        $nonMan = array(
            $query,
 
            'onlyStock'     => $showOnlyStock,
 
            'category'      => $selectedCategory,
 
            'modelNumber'   => $selectedModelNumber,
 
            'partTechSpecs' => array(),
 
            'sort'          => array(
                'manufacturer' => 'ASC',
                'partNumber'   => 'ASC'
            )
        );
    
    
    
    if ($selectedTechSpecs) {
      foreach ($selectedTechSpecs as $selectedTechSpecName => $selectedTechSpecValue) {
        if (!$selectedTechSpecName) {
          continue;
        }
        
        $selectedTechSpecName = preg_replace('/[^a-z0-9]+/i', '', $selectedTechSpecName);
        
        $searchParameters['partTechSpecs'][$selectedTechSpecName] = $selectedTechSpecValue;
      }
    }
    
    
    
    
    
    $results = \Models\Parts::search(
      $searchParameters,
      $pageNumber,
      $itemsPerPage
    );
    
    
    
    $parts        = $results['parts'];
    $totalResults = $results['totalResults'];
    
    
    
    
    
    $url = '/category/' . $selectedSectionURL . '/' . $selectedCategoryURL . '/';
    
    
    $url .= '?' . http_build_query(
      array(
        'q'            => $query,
        
        'onlyStock'    => $showOnlyStock,
        'manufacturer' => $selectedManufacturerURL,
        'modelNumber'  => $selectedModelNumberID,
        
        'itemsPerPage' => $itemsPerPage,
        
        'page'         => ''
      ),
      null,
      '&amp;'
    );
    
    
    $sections = \Models\Sections::find(
      array(
        'order' => 'section ASC'
      )
    );
    
    $categories = $selectedSection->getCategories(
      array(
        'order' => 'category ASC'
      )
    );
    
    $subCategories = $selectedCategory->getSubCategories(
      array(
        'order' => 'subCategory ASC'
      )
    );
    
    
    
    //TODO
    $filterManufacturers = \Models\Parts::searchDistinct('manufacturerID', $nonMan, 1000);
    
    $filterSystemManufacturers = \Models\Parts::searchDistinct('systemManufacturerID',  $searchParameters, 1000);
    $filterModelFamilies       = \Models\Parts::searchDistinct('modelFamilyID',         $searchParameters, 1000);
    $filterModelNumbers        = \Models\Parts::searchDistinct('modelNumberID',         $searchParameters, 1000);
    
    
    
    $manufacturers = \Models\Manufacturers::find(
      array(
        'order' => 'manufacturer ASC'
      )
    );
    
    
    
    $techSpecs = \Models\Parts::searchTechSpecs($searchParameters);
    
    
    
    $this->view->sections      = $sections;
    $this->view->categories    = $categories;
    $this->view->subCategories = $subCategories;
    $this->view->techSpecs     = $techSpecs;
    
    
    
    
    $this->view->filterManufacturers = $filterManufacturers;
    
    
    $this->view->filterSystemManufacturers = $filterSystemManufacturers;
    $this->view->filterModelFamilies       = $filterModelFamilies;
    $this->view->filterModelNumbers        = $filterModelNumbers;
    
    
    
    $this->view->manufacturers = $manufacturers;
    
    
    $this->view->parts = $parts;
    
    $this->view->selectedSection      = $selectedSection;
    $this->view->selectedCategory     = $selectedCategory;
    $this->view->selectedManufacturer = $selectedManufacturer;
    $this->view->selectedTechSpecs    = $selectedTechSpecs;
    $this->view->selectedModelNumber  = $selectedModelNumber;
    
    $this->view->itemsPerPage = $itemsPerPage;
    
    $this->view->query           = $query;
    $this->view->manufacturerURL = @$manufacturerURL;
    
    $this->view->showOnlyStock = $showOnlyStock;
    
    $this->view->totalResults = $totalResults;
    
    
    
    $this->view->pagination = array(
      'pageNumber'   => $pageNumber,
      'itemsPerPage' => $itemsPerPage,
      'totalItems'   => $totalResults,
      'url'          => $url,
      'what'         => 'product'
    );
    
    
    
    $this->tag->prependTitle(
      $selectedCategory->getCategory() . ' parts | '
    );
 
 
 
    //Show Special offers on the side of search reults.
 
    $d = \Models\Metadata::findFirst('category = "promotional" and name = "distributors"');
    $distis = json_decode($d->getValue(), true);
    //get selected category
    $getCat = $selectedCategory->getCategoryURL();
        $cats = /*again get from database, controlled by dashboard. */array($getCat);
        $manus = array();
 
         $fs = array(
          'distributor_urls' => $distis,
      'category_urls' => $cats,
          'manufacturer_urls' => $manus
        );
 
 
        //get 4 random special offers per page, including a mail shot entry if we need to.
        $msi = mt_rand(0, 4);
        $template = new \Template();
        $template->setTemplateDir('offer-views');
 
        $offers = \Models\PromoParts::getSitePromotions($fs, 2, 1, false);
 
        //render the html for the selected offers.
        //format the params.
        $params = array();
        if(count($offers > 0)) {
            for($i = 0; $i < count($offers); $i++) {
                if($i == $msi) {
                    //enter this first at this index.
                    $params[] = array(
                        'type' => 'mailshot',
                        'object' => null
                    );
                }
                $params[] = array('type' => 'promo', 'object' => $offers[$i]);
            }
        }
 
        $html = $template->renderTemplate('offers', 'home', array('objects' => $params));
 
        $this->view->initialOffers = $html;
 
  }
  
  
  
  public function subCategoryAction()
  {
    $selectedSectionURL     = $this->dispatcher->getParam('sectionURL',     'urlSlug');
    $selectedCategoryURL    = $this->dispatcher->getParam('categoryURL',    'urlSlug');
    $selectedSubCategoryURL = $this->dispatcher->getParam('subCategoryURL', 'urlSlug');
    
    $pageNumber = $this->request->getQuery('page') ?: 1;
    
    $query = $this->request->getQuery('q', 'trim');
    
    $selectedManufacturerURL = $this->request->getQuery('manufacturer', 'urlSlug');
    $selectedModelNumberID   = $this->request->getQuery('modelNumber',  'int');
    
    
    
    $selectedTechSpecs = $this->request->getQuery('techSpecs');
    
    
    
    $showOnlyStock = !!$this->request->getQuery('onlyStock');
    
    
    
    
    
    $itemsPerPage = $this->request->getQuery('itemsPerPage', 'int', 10);
    
    
    
    // Force a hard maximum of 100 for the items per page.
    $itemsPerPage = min($itemsPerPage, 100);
    
    
    
    
    
    
    
    
    $selectedSubCategory = \Models\SubCategories::findFromURL(
      $selectedSectionURL,
      $selectedCategoryURL,
      $selectedSubCategoryURL
    );
 
 
    
    if (!$selectedSubCategory) {
      return $this->error404();
    }
    
    $selectedCategory = $selectedSubCategory->getCategories();
    $selectedSection  = $selectedCategory->getSections();
    
    
    
    
    
    $selectedManufacturer = ($selectedManufacturerURL)
        ? \Models\Manufacturers::findFromURL($selectedManufacturerURL)
        : false;
    
    $selectedModelNumber = ($selectedModelNumberID)
        ? \Models\ModelNumbers::findFirstByModelNumberID($selectedModelNumberID)
        : false;
    
    
    
    
    
    $searchParameters = array(
      $query,
      
      'onlyStock'     => $showOnlyStock,
      
      'section'       => $selectedSection,
      'category'      => $selectedCategory,
      'subCategory'   => $selectedSubCategory,
      
      'manufacturer'  => $selectedManufacturer,
      'modelNumber'   => $selectedModelNumber,
      
      'partTechSpecs' => array()
    );
 
        $nonMan = array(
            $query,
 
            'onlyStock'     => $showOnlyStock,
 
            'section'       => $selectedSection,
            'category'      => $selectedCategory,
            'subCategory'   => $selectedSubCategory,
 
            'modelNumber'   => $selectedModelNumber,
 
            'partTechSpecs' => array()
        );
    
    
    
    if ($selectedTechSpecs) {
      foreach ($selectedTechSpecs as $selectedTechSpecName => $selectedTechSpecValue) {
        if (!$selectedTechSpecName) {
          continue;
        }
        
        $selectedTechSpecName = preg_replace('/[^a-z0-9]+/i', '', $selectedTechSpecName);
        
        $searchParameters['partTechSpecs'][$selectedTechSpecName] = $selectedTechSpecValue;
      }
    }
    
    
    
    
    
    $results = \Models\Parts::search(
      $searchParameters,
      $pageNumber,
      $itemsPerPage
    );
    
    
    
    $parts        = $results['parts'];
    $totalResults = $results['totalResults'];
    
    
    
    
    
    $url = '/category/' . $selectedSectionURL . '/' . $selectedCategoryURL . '/' . $selectedSubCategoryURL . '/';
    
    
    $url .= '?' . http_build_query(
      array(
        'q'            => $query,
        
        'onlyStock'    => $showOnlyStock,
        'manufacturer' => $selectedManufacturerURL,
        'modelNumber'  => $selectedModelNumberID,
        
        'itemsPerPage' => $itemsPerPage,
        
        'page'         => ''
      ),
      null,
      '&amp;'
    );
    
    
    $sections = \Models\Sections::find(
      array(
        'order' => 'section ASC'
      )
    );
    
    $categories = $selectedSection->getCategories(
      array(
        'order' => 'category ASC'
      )
    );
    
    $subCategories = $selectedCategory->getSubCategories(
      array(
        'order' => 'subCategory ASC'
      )
    );
    
    
    
    //TODO
    $filterManufacturers = \Models\Parts::searchDistinct('manufacturerID', $nonMan, 1000);
    
    $filterSystemManufacturers = \Models\Parts::searchDistinct('systemManufacturerID',  $searchParameters, 1000);
    $filterModelFamilies       = \Models\Parts::searchDistinct('modelFamilyID',         $searchParameters, 1000);
    $filterModelNumbers        = \Models\Parts::searchDistinct('modelNumberID',         $searchParameters, 1000);
    
    
    
    $manufacturers = \Models\Manufacturers::find(
      array(
        'order' => 'manufacturer ASC'
      )
    );
    
    
    
    $techSpecs = \Models\Parts::searchTechSpecs($searchParameters);
    
    
    
    $this->view->sections      = $sections;
    $this->view->categories    = $categories;
    $this->view->subCategories = $subCategories;
    $this->view->techSpecs     = $techSpecs;
    
    
    
    
    $this->view->filterManufacturers = $filterManufacturers;
    
    
    $this->view->filterSystemManufacturers = $filterSystemManufacturers;
    $this->view->filterModelFamilies       = $filterModelFamilies;
    $this->view->filterModelNumbers        = $filterModelNumbers;
    
    
    
    $this->view->manufacturers = $manufacturers;
    
    
    $this->view->parts = $parts;
    
    $this->view->selectedSection      = $selectedSection;
    $this->view->selectedCategory     = $selectedCategory;
    $this->view->selectedSubCategory  = $selectedSubCategory;
    $this->view->selectedManufacturer = $selectedManufacturer;
    $this->view->selectedTechSpecs    = $selectedTechSpecs;
    $this->view->selectedModelNumber  = $selectedModelNumber;
    
    $this->view->itemsPerPage = $itemsPerPage;
    
    $this->view->query           = $query;
    $this->view->manufacturerURL = $manufacturerURL;
    
    $this->view->showOnlyStock = $showOnlyStock;
    
    $this->view->totalResults = $totalResults;
    
    
    
    $this->view->pagination = array(
      'pageNumber'   => $pageNumber,
      'itemsPerPage' => $itemsPerPage,
      'totalItems'   => $totalResults,
      'url'          => $url,
      'what'         => 'product'
    );
    
    
    
    $this->tag->prependTitle(
      $selectedSubCategory->getSubCategory() . ' parts | '
    );
  
    //Show Special offers on the side of search reults.
    $d = \Models\Metadata::findFirst('category = "promotional" and name = "distributors"');
    $distis = json_decode($d->getValue(), true);
    //get selected category
    $getCat = $selectedCategory->getCategoryURL();
        $cats = /*again get from database, controlled by dashboard. */array($getCat);
        $manus = array();
 
         $fs = array(
          'distributor_urls' => $distis,
      'category_urls' => $cats,
          'manufacturer_urls' => $manus
        );
 
        //get 4 random special offers per page, including a mail shot entry if we need to.
        $msi = mt_rand(0, 4);
        $template = new \Template();
        $template->setTemplateDir('offer-views');
 
        $offers = \Models\PromoParts::getSitePromotions($fs, 2, 1, false);
 
        //render the html for the selected offers.
        //format the params.
        $params = array();
        if(count($offers > 0)) {
            for($i = 0; $i < count($offers); $i++) {
                if($i == $msi) {
                    //enter this first at this index.
                    $params[] = array(
                        'type' => 'mailshot',
                        'object' => null
                    );
                }
                $params[] = array('type' => 'promo', 'object' => $offers[$i]);
            }
        }
 
        $html = $template->renderTemplate('offers', 'home', array('objects' => $params));
 
        $this->view->initialOffers = $html;
 
  }
}
#11Controllers\CategoryController->subCategoryAction(networking, networking-routers, cisco-routers)
#12Phalcon\Dispatcher->dispatch()
#13Phalcon\Mvc\Application->handle()
/home/ubuntu/partsit-site/app/web.php (424)
<?php
 
define('BOOTSTRAP', 'WEB');
 
if (isset($_SERVER['HTTP_X_VARNISH']) && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
 
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'http') {
  header("Location: https://www.partsit.com{$_SERVER['REQUEST_URI']}");
}
 
// if(geoip_country_code_by_name($_SERVER['REMOTE_ADDR']) == 'CN') {
//   header("HTTP/1.0 404 Not Found");
//   return;
// }
 
require 'startup.php';
 
if (ENVIRONMENT == 'development') {
  $debug = new \Phalcon\Debug();
  $debug->listen();
}
 
$di->set('markdown', function($view, $di){
  $engine = new \Engine\Markdown($view, $di);
  return $engine;
});
 
 
// Setting up the view component.
$di->set(
  'view',
  function () use ($config, $di) {
    $view = new \Phalcon\Mvc\View();
    
    $view->setViewsDir($config->application->viewsDir);
    
    // Set the volt templating engine.
    $view->registerEngines(
      array(
        '.volt' => 'volt',
        '.md'   => 'markdown'
      )
    );
    
    return $view;
  }
);
 
$di->set(
  'console',
  function() {
    $console = new \Console();
    return $console;
  },true
);
 
$di->set(
  'cookies',
  function () {
    $cookies = new \Phalcon\Http\Response\Cookies();
    
    $cookies->useEncryption(false);
    
    return $cookies;
  }
);
 
$di->set(
  'viewCache',
  function () use ($config) {
    $frontCache = new \Phalcon\Cache\Frontend\Output(
      array(
        'lifetime' => $config->cache->viewCache
      )
    );
 
    $cache = new \Phalcon\Cache\Backend\APC(
      $frontCache,
      array(
        'prefix' => 'viewCache:'
      )
    );
    
    return $cache;
  }
);
 
$di->set(
  'flash',
  function () {
    $flash = new \Phalcon\Flash\Direct(
      array(
        'error'   => 'alert alert-danger',
        'success' => 'alert alert-success',
        'notice'  => 'alert alert-info',
        'warning' => 'alert alert-warning'
      )
    );
    
    return $flash;
  }
);
 
// Register the flash service with Twitter Bootstrap classes.
$di->set(
  'flashSession',
  function () {
    $flash = new \Phalcon\Flash\Session(
      array(
        'error'   => 'alert alert-danger',
        'success' => 'alert alert-success',
        'notice'  => 'alert alert-info',
        'warning' => 'alert alert-warning'
      )
    );
    
    return $flash;
  }
);
 
// Set custom dispatcher to catch events in the dispatch loop.
$di->set(
  'dispatcher',
  function () use ($di) {
    $dispatcher = new \Phalcon\Mvc\Dispatcher();
    
    $dispatcher->setDefaultNamespace('\Controllers');
    
    // Obtain the standard eventsManager from the DI.
    $eventsManager = $di->getShared('eventsManager');
    
    // Attach a listener for type "dispatch".
    $eventsManager->attach(
      'dispatch:beforeDispatchLoop',
      function ($event, $dispatcher) {
        if ($dispatcher->getActionName()) {
          $actionName = lcfirst(\Phalcon\Text::camelize($dispatcher->getActionName()));
          
          $dispatcher->setActionName($actionName);
        }
      }
    );
    
    $eventsManager->attach(
      'dispatch:beforeException',
      function ($event, $dispatcher, $exception) {
        switch ($exception->getCode()) {
          case \Phalcon\Mvc\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
          case \Phalcon\Mvc\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
            $dispatcher->forward(
              array(
                'controller' => 'error',
                'action'     => 'route404'
              )
            );
            
            return false;
        }
      }
    );
    
    $dispatcher->setEventsManager($eventsManager);
    
    return $dispatcher;
  },
  true
);
 
$di->set(
  'url',
  function () {
    $url = new \Phalcon\Mvc\Url();
    
    // if (ENVIRONMENT == 'production') {
    //   $url->setBaseUri('http://www.partsit.com/');
    // }
    
    return $url;
  }
);
 
/*
 * Start the session the first time when some component request the session
 * service.
 */
$di->setShared(
  'session',
  function () {
    $session = new \Phalcon\Session\Adapter\Files();
    
    $session->start();
    
    return $session;
  }
);
 
$di->set(
  'logger',
  function () use ($logger) {
    return $logger;
  }
);
 
// Set up custom routes in the application.
$di->set(
  'router',
  function () {
    $router = new \Phalcon\Mvc\Router(false);
    
    $router->removeExtraSlashes(true);
    
    $router->add(
      '/:controller',
      array(
        'controller' => 1
      )
    );
    
    $router->add(
      '/:controller/([a-zA-Z0-9\-]+)/:params',
      array(
        'controller' => 1,
        'action'     => 2,
        'params'     => 3
      )
    )->convert(
      'action',
      function ($action) {
        return \Phalcon\Text::camelize($action);
      }
    );
 
    $router->add('/applegate.csv', 'applegate::index');
    //$router->add('/sitemap.xml',    'sitemap::file');
 
    //add all of the old links, and redirect to new equivilent
    $router->add('/support/sitemap', 'sitemap::index');
    $router->add('/support/partsit-com-rma-returns-request-terms-conditions', 'support::returnsPolicy');
    $router->add('/support/cookies-policy', 'support::cookiePolicy');
    $router->add('/support/public-sector-credit-account', 'public-sector-credit-account::index');
    $router->add('/support/about-partsit-com', 'about::index');
    
    $router->add('/consumers',   'index::consumers');
    $router->add('/maintenance', 'index::maintenance');
    $router->add('/end-users',   'index::endUsers');
    $router->add('/government',  'index::government');
    $router->add('/brokers',     'index::brokers');
    
    $router->add('/categories-ajax/{sectionURL:[a-z0-9\-]+}',                           'categories-ajax::section');
    $router->add('/categories-ajax/{sectionURL:[a-z0-9\-]+}/{categoryURL:[a-z0-9\-]+}', 'categories-ajax::category');
    
    /* chat.chat route */
    $router->add('/chat/{chatID:[0-9]+}', 'chat::chat');
    
    /* product route */
    $router->add('/product/{partURL:[a-z0-9\-]+}',               'product::product');
    $router->add('/product/{partURL:[a-z0-9\-]+}/email',         'product::email');
    $router->add('/product/{partURL:[a-z0-9\-]+}/request-price', 'product::requestPrice');
    $router->add('/product/{partURL:[a-z0-9\-]+}/review',        'product::review');
    $router->add('/product/{partURL:[a-z0-9\-]+}/email-example', 'product::emailExample');
    $router->add('/product/{partURL:[a-z0-9\-]+}/refurb',      'product::refurb');
    
    $router->add(
      '/product/{partURL:[a-z0-9\-]+}/admin/([a-zA-Z0-9\-]+)',
      array(
        'controller' => 'product-admin',
        'action'     => 2
      )
    );
 
    /* products route */
    $router->add('/products/{partURL:[a-z0-9\-]+}',               'product::product');
    $router->add('/products/{partURL:[a-z0-9\-]+}/email',         'product::email');
    $router->add('/products/{partURL:[a-z0-9\-]+}/request-price', 'product::requestPrice');
    $router->add('/products/{partURL:[a-z0-9\-]+}/review',        'product::review');
    $router->add('/products/{partURL:[a-z0-9\-]+}/email-example', 'product::emailExample');
    
    $router->add(
      '/products/{partURL:[a-z0-9\-]+}/admin/([a-zA-Z0-9\-]+)',
      array(
        'controller' => 'product-admin',
        'action'     => 2
      )
    );
 
    $router->add('/resources/image/{hash:[a-zA-Z0-9\-]+}', 'resources::image');
    $router->add('/resources/blob/{partID:[0-9]+}', 'resources::blob');
        
        $router->add('/background/alibaba/{partURL:[a-z0-9\-]+}',    'background::alibaba');
    
    $router->add('/reviews/{partURL:[a-z0-9\-]+}',               'reviews::product');
    
    /* part-finder routes */
    $router->add('/part-finder/{systemManufacturerURL:[a-z0-9\-]+}',                                                                                     'part-finder::manufacturer');
    $router->add('/part-finder/{systemManufacturerURL:[a-z0-9\-]+}/{modelFamilyURL:[a-z0-9\-]+}',                                                        'part-finder::modelFamily');
    $router->add('/part-finder/{systemManufacturerURL:[a-z0-9\-]+}/{modelFamilyURL:[a-z0-9\-]+}/{modelNumberURL:[a-z0-9\-]+}',                           'part-finder::modelNumber');
    $router->add('/part-finder/{systemManufacturerURL:[a-z0-9\-]+}/{modelFamilyURL:[a-z0-9\-]+}/{modelNumberURL:[a-z0-9\-]+}/{oldCategorySlug:[a-z-]+}', 'part-finder::modelNumber');
    $router->add('/part-finder/search',                                                                                                                  'part-finder::search');
    
    $router->add('/account/returns/{pageNumber:[0-9]+}',          'account::returns');
    
    $router->add('/account/previous-purchases/{pageNumber:[0-9]+}',          'account::previousPurchases');
    $router->add('/account/quotes/{pageNumber:[0-9]+}',                      'account::quotes');
    $router->add('/account/invoices/{pageNumber:[0-9]+}',                    'account::invoices');
    
    /* part-finder admin routes */
    $router->add('/dashboard/part-finder/{systemManufacturerURL:[a-z0-9\-]+}',                                                           'dashboard::partFinderManufacturer');
    $router->add('/dashboard/part-finder/{systemManufacturerURL:[a-z0-9\-]+}/{modelFamilyURL:[a-z0-9\-]+}',                              'dashboard::partFinderModelFamily');
    $router->add('/dashboard/part-finder/{systemManufacturerURL:[a-z0-9\-]+}/{modelFamilyURL:[a-z0-9\-]+}/{modelNumberURL:[a-z0-9\-]+}', 'dashboard::partFinderModelNumber');
    
    $router->add('/dashboard/categories/{sectionURL:[a-z0-9\-]+}',                                                        'dashboard::categoriesSection');
    $router->add('/dashboard/categories/{sectionURL:[a-z0-9\-]+}/{categoryURL:[a-z0-9\-]+}',                              'dashboard::categoriesCategory');
    
    $router->add('/dashboard/promo/{page:[0-9]+}', 'dashboard::promo');
    $router->add('/dashboard/pending-orders/{page:[0-9]+}', 'dashboard::pendingOrders');
    $router->add('/dashboard/pending-orders/{page:[0-9]+}/{orderDirection:[0-1]{1}}', 'dashboard::pendingOrders');
 
    $router->add('/api/xml/all', 'api::xmlAll');
 
    $router->add('/dashboard/feedback/{pageNumber:[0-9]+}',      'dashboard::feedback');
    $router->add('/dashboard/feedback/add',                      'dashboard::feedbackAdd');
    $router->add('/dashboard/feedback/edit/{feedbackID:[0-9]+}', 'dashboard::feedbackEdit');
    
    //TODO
    $router->add('/dashboard/email-shots/new', 'dashboard::emailShotsNew');
    $router->add('/dashboard/approve-images/{s:[A-Za-z0-9]+}', 'dashboard::approveImages');
    
    $router->add('/dashboard/parts/add',                             'dashboard::partsAdd');
    $router->add('/dashboard/parts/modify/{partURL:[A-Za-z0-9\-]+}', 'dashboard::partsModify');
    
    $router->add('/dashboard/accounts/{pageNumber:[0-9]+}',          'dashboard::accounts');
    $router->add('/dashboard/accounts/orders/{customerID:[0-9]+}',   'dashboard::accountsOrders');
    $router->add('/dashboard/accounts/rmas/{customerID:[0-9]+}',     'dashboard::accountsRmas');
    
    $router->add('/dashboard/survey/add', 'dashboard::surveyAdd');
    $router->add('/dashboard/newsletter-subscribers/{page:[0-9]+}', 'dashboard::newsletterSubscribers');
    
    $router->add('/dashboard/carousel/add',                        'dashboard::carouselAdd');
    $router->add('/dashboard/carousel/edit/{carouselID:[0-9]+}',   'dashboard::carouselEdit');
    $router->add('/dashboard/carousel/delete/{carouselID:[0-9]+}', 'dashboard::carouselDelete');
    
    $router->add('/dashboard/online-users/{pageNumber:[0-9]+}', 'dashboard::onlineUsers');
    
    $router->add('/dashboard/reviews/{pageNumber:[0-9]+}', 'dashboard::reviews');
    
    $router->add('/dashboard/part-images/{pageNumber:[0-9]+}', 'dashboard::partImages');
    
    $router->add('/dashboard/pending-orders/search/{pendingOrderID:[0-9]+}', 'dashboard::pendingOrdersSingle');
    
    $router->add('/dashboard/showcases/edit/{showcaseURL:[a-z0-9\-]+}', 'dashboard::showcasesEdit');
    
    $router->add('/manufacturers/{letter:[a-z]}',                     'manufacturers::letter');
    
    /* manufacturer */
    $router->add('/manufacturer/{manufacturerURL:[a-z0-9\-]+}',                     'manufacturer::manufacturer');
    $router->add('/manufacturer/{manufacturerURL:[a-z0-9\-]+}/{pageNumber:[0-9]+}', 'manufacturer::manufacturer');
    
    /* category routes. */
    $router->add('/category/{sectionURL:[a-z0-9\-]+}',                                                                            'category::section');
    $router->add('/category/{sectionURL:[a-z0-9\-]+}/{categoryURL:[a-z0-9\-]+}',                                                  'category::category');
    $router->add('/category/{sectionURL:[a-z0-9\-]+}/{categoryURL:[a-z0-9\-]+}/{subCategoryURL:[a-z0-9\-]+}',                     'category::subCategory');
    $router->add('/category/{sectionURL:[a-z0-9\-]+}/{categoryURL:[a-z0-9\-]+}/{subCategoryURL:[a-z0-9\-]+}/{pageNumber:[0-9]+}', 'category::subCategory');
    
    /* basket.add routes. */
    $router->add('/basket/add/{partURL:[a-z0-9\-]+}',                   'basket::add');
    $router->add('/basket/add/{partURL:[a-z0-9\-]+}/{quantity:[0-9]+}', 'basket::add');
    $router->add('/basket/add-bundle/{partBundleID:[0-9]+}',            'basket::addBundle');
    
    /* basket.remove */
    $router->add('/basket/remove/{partURL:[a-z0-9\-]+}', 'basket::remove');
    
    $router->add('/feedback/{pageNumber:[0-9]+}', 'feedback::index');
    
    $router->add('/returns/{orderID:[a-zA-Z0-9\-]+}',        'returns::return');
    $router->add('/returns/{orderID:[a-zA-Z0-9\-]+}/submit', 'returns::submit');
    
    $router->add('/res/css/{cssFile:[A-Za-z0-9\-\.]+}', 'res::css');
    $router->add('/res/js/{jsFile:[A-Za-z0-9\-\.]+}',   'res::js');
    
    $router->add('/review/{reviewID:[0-9]+}/helpful',   'reviews::helpful');
    $router->add('/review/{reviewID:[0-9]+}/unhelpful', 'reviews::unhelpful');
    
    $router->add('/survey/{surveyURL:[A-Za-z0-9-]+}', 'survey::survey');
    
    /* dashboard.staff-ips routes. */
    $router->add('/dashboard/staff-ips/add',                               'dashboard::staffIpsAdd');
    $router->addPost('/background/update-staff-ip-status',                 'dashboard::updateIPStatus');
    $router->add('/dashboard/staff-ips/edit/{staffIpURL:[A-Za-z0-9-]+}',   'dashboard::staffIpsEdit');
    $router->add('/dashboard/staff-ips/delete/{staffIpURL:[A-Za-z0-9-]+}', 'dashboard::staffIpsDelete');
    
    $router->add('/showcase/{showcaseURL:[a-z0-9\-]+}', 'showcase::showcase');
    
    $router->add('/sitemap/xml/pages',                     'sitemap::xmlPages');
    $router->add('/sitemap/xml/manufacturers',             'sitemap::xmlManufacturers');
    $router->add('/sitemap/xml/parts/{pageNumber:[0-9]+}', 'sitemap::xmlParts');
    
    $router->add('/guides/{guideURL:[A-Za-z0-9-]+}', 'guides::guide');
    
    $router->add('/api/xml/{page:[0-9]+}', 'api::xml');
    
    $router->add('/clearance/{manufacturerURL:[A-Za-z0-9-]+}',                     'clearance::manufacturer');
    $router->add('/clearance/{manufacturerURL:[A-Za-z0-9-]+}/{pageNumber:[0-9]+}', 'clearance::manufacturer');
    
    $router->add('/tracking/{orderID:[0-9]+}', 'tracking::details');
    
    return $router;
  }
);
 
// Handle the request.
$application = new \Phalcon\Mvc\Application($di);
 
$eventsManager = new \Phalcon\Events\Manager();
$application->setEventsManager($eventsManager);
 
$eventsManager->attach("application:boot", new \PhalconHelpers\ServiceProvider\Aws(
  array(
    'region' => 'eu-west-1'
  )
));
 
echo $application->handle()->getContent();
 
// Just in case there's nothing to commit.
try {
  $logger->commit();
} catch (\Exception $e) {
  
}
#14require_once(/home/ubuntu/partsit-site/app/web.php)
/home/ubuntu/partsit-site/public/index.php (3)
<?php
 
require_once '../app/web.php';
KeyValue
_url/category/networking/networking-routers/cisco-routers
KeyValue
USERwww-data
HOME/var/www
FCGI_ROLERESPONDER
QUERY_STRING_url=/category/networking/networking-routers/cisco-routers
REQUEST_METHODGET
CONTENT_TYPE
CONTENT_LENGTH
SCRIPT_FILENAME/home/ubuntu/partsit-site/public/index.php
SCRIPT_NAME/index.php
REQUEST_URI/category/networking/networking-routers/cisco-routers
DOCUMENT_URI/index.php
DOCUMENT_ROOT/home/ubuntu/partsit-site/public
SERVER_PROTOCOLHTTP/1.1
GATEWAY_INTERFACECGI/1.1
SERVER_SOFTWAREnginx/1.4.6
REMOTE_ADDR172.31.32.5
REMOTE_PORT55563
SERVER_ADDR172.31.16.243
SERVER_PORT443
SERVER_NAME
HTTPSon
REDIRECT_STATUS200
PATH_INFO
PATH_TRANSLATED/home/ubuntu/partsit-site/public
HTTP_HOSTwww.partsit.com
HTTP_ACCEPTtext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_ENCODINGgzip
HTTP_IF_MODIFIED_SINCEMon, 22 Oct 2018 12:43:37 UTC
HTTP_USER_AGENTCCBot/2.0 (https://commoncrawl.org/faq/)
HTTP_X_FORWARDED_FOR54.164.198.240
HTTP_X_FORWARDED_PORT443
HTTP_X_FORWARDED_PROTOhttps
HTTP_CONNECTIONkeep-alive
PHP_SELF/index.php
REQUEST_TIME_FLOAT1544440574.2047
REQUEST_TIME1544440574
#Path
0/home/ubuntu/partsit-site/public/index.php
1/home/ubuntu/partsit-site/app/web.php
2/home/ubuntu/partsit-site/app/startup.php
3/home/ubuntu/partsit-site/vendor/autoload.php
4/home/ubuntu/partsit-site/vendor/composer/autoload_real.php
5/home/ubuntu/partsit-site/vendor/composer/ClassLoader.php
6/home/ubuntu/partsit-site/vendor/composer/include_paths.php
7/home/ubuntu/partsit-site/vendor/composer/autoload_namespaces.php
8/home/ubuntu/partsit-site/vendor/composer/autoload_psr4.php
9/home/ubuntu/partsit-site/vendor/composer/autoload_classmap.php
10/home/ubuntu/partsit-site/vendor/composer/autoload_files.php
11/home/ubuntu/partsit-site/vendor/guzzlehttp/promises/src/functions_include.php
12/home/ubuntu/partsit-site/vendor/guzzlehttp/promises/src/functions.php
13/home/ubuntu/partsit-site/vendor/guzzlehttp/psr7/src/functions_include.php
14/home/ubuntu/partsit-site/vendor/guzzlehttp/psr7/src/functions.php
15/home/ubuntu/partsit-site/vendor/guzzlehttp/guzzle/src/functions_include.php
16/home/ubuntu/partsit-site/vendor/guzzlehttp/guzzle/src/functions.php
17/home/ubuntu/partsit-site/vendor/mtdowling/jmespath.php/src/JmesPath.php
18/home/ubuntu/partsit-site/vendor/aws/aws-sdk-php/src/functions.php
19/home/ubuntu/partsit-site/vendor/swiftmailer/swiftmailer/lib/swift_required.php
20/home/ubuntu/partsit-site/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php
21/home/ubuntu/partsit-site/vendor/ircmaxell/password-compat/lib/password.php
22/home/ubuntu/partsit-site/vendor/symfony/polyfill-php55/bootstrap.php
23/home/ubuntu/partsit-site/vendor/symfony/polyfill-php54/bootstrap.php
24/home/ubuntu/partsit-site/vendor/react/promise/src/functions_include.php
25/home/ubuntu/partsit-site/vendor/react/promise/src/functions.php
26/home/ubuntu/partsit-site/vendor/symfony/polyfill-mbstring/bootstrap.php
27/home/ubuntu/partsit-site/vendor/illuminate/support/Illuminate/Support/helpers.php
28/home/ubuntu/partsit-site/app/library/PhalconHelpers/ServiceProvider/Aws.php
29/home/ubuntu/partsit-site/app/controllers/CategoryController.php
30/home/ubuntu/partsit-site/app/controllers/ControllerBase.php
31/home/ubuntu/partsit-site/app/library/Auth.php
32/home/ubuntu/partsit-site/app/models/StaffIPs.php
33/home/ubuntu/partsit-site/app/models/BaseModel.php
34/home/ubuntu/partsit-site/app/models/Baskets.php
35/home/ubuntu/partsit-site/app/models/ExchangeRates.php
36/home/ubuntu/partsit-site/app/models/Sections.php
37/home/ubuntu/partsit-site/app/models/Metadata.php
38/home/ubuntu/partsit-site/app/models/Manufacturers.php
39/home/ubuntu/partsit-site/app/models/SystemManufacturers.php
40/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/MD5.php
41/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/IPv4.php
42/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/URLSlug.php
43/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/Description.php
44/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/Month.php
45/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/DellServiceTag.php
46/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/HPProductNumber.php
47/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/LenovoSerialNumber.php
48/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/CreditCardNumber.php
49/home/ubuntu/partsit-site/app/library/PhalconHelpers/Filters/Name.php
50/home/ubuntu/partsit-site/app/models/SubCategories.php
51/home/ubuntu/partsit-site/app/models/Categories.php
52/home/ubuntu/partsit-site/app/models/Parts.php
53/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/ClientBuilder.php
54/home/ubuntu/partsit-site/vendor/psr/log/Psr/Log/NullLogger.php
55/home/ubuntu/partsit-site/vendor/psr/log/Psr/Log/AbstractLogger.php
56/home/ubuntu/partsit-site/vendor/psr/log/Psr/Log/LoggerInterface.php
57/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php
58/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php
59/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php
60/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Client/Middleware.php
61/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Serializers/SmartSerializer.php
62/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Serializers/SerializerInterface.php
63/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/ConnectionFactory.php
64/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/ConnectionFactoryInterface.php
65/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/Selectors/RoundRobinSelector.php
66/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/Selectors/SelectorInterface.php
67/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php
68/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/ConnectionInterface.php
69/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/StaticNoPingConnectionPool.php
70/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/AbstractConnectionPool.php
71/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/ConnectionPool/ConnectionPoolInterface.php
72/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Transport.php
73/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Client.php
74/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/IndicesNamespace.php
75/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/AbstractNamespace.php
76/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/ClusterNamespace.php
77/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/NodesNamespace.php
78/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/SnapshotNamespace.php
79/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Namespaces/CatNamespace.php
80/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/Search.php
81/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/AbstractEndpoint.php
82/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Core.php
83/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php
84/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php
85/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php
86/home/ubuntu/partsit-site/vendor/react/promise/src/PromiseInterface.php
87/home/ubuntu/partsit-site/vendor/react/promise/src/PromisorInterface.php
88/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php
89/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php
90/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php
91/home/ubuntu/partsit-site/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php
92/home/ubuntu/partsit-site/vendor/react/promise/src/FulfilledPromise.php
93/home/ubuntu/partsit-site/vendor/react/promise/src/ExtendedPromiseInterface.php
94/home/ubuntu/partsit-site/vendor/react/promise/src/CancellablePromiseInterface.php
95/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Common/Exceptions/BadRequest400Exception.php
96/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Common/Exceptions/ElasticsearchException.php
97/home/ubuntu/partsit-site/vendor/psr/log/Psr/Log/LogLevel.php
98/home/ubuntu/partsit-site/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Common/Exceptions/Missing404Exception.php
99/home/ubuntu/partsit-site/vendor/react/promise/src/RejectedPromise.php
Memory
Usage2883584